Soy muy nuevo en Angular. Tengo algo de trabajo en Angular.
Necesito vincular la lista desplegable anidada para los datos Json que provienen del servidor llamando a Rest Api.
Los datos tienen un atributo Nivel. Especifica el nivel en la jerarquía del grupo. El padre tendrá Child=1 , Grandchild=2 y así sucesivamente. Child and Grandchild tiene un campo de grupo, que muestra dentro de qué menú principal estará el menú secundario.
Lo intenté desarrollar, pero encontré todos los ejemplos de bootstrap con datos estáticos en un archivo html y un archivo CSS separado que me resultó complicado.
Quiero hacerlo dinámicamente usando TypeScript . ¿Cómo puedo empezar a trabajar en él.
Esta es una muestra codificada que necesita según los datos de nivel anidados de sus datos json. Ahora puede realizar un bucle forzado de los datos json formateados en el DOM utilizando datos del modelo . Espero que esto te ayude a crear un menú desplegable de varios niveles.
groupBy(xs, key) { return xs.reduce(function (rv, x) { (rv[x[key]] = rv[x[key]] || []).push(x); return rv; }, {}); } var model; getData() { var sampleData = { "ArrayOfLocationGroup": { "LocationGroup": [ ... ...//Server response data ], "_xmlns:xsd": "http://www.w3.org/2001/XMLSchema" } } var list = this.sampleData["ArrayOfLocationGroup"]["LocationGroup"]; var formattedList = []; list.forEach(element => { var obj = { //Make sure your server response data to like this structure "Id": element.Id, "Name": element.Name, "GroupId": element.GroupId.__text, "ParentLocationGroup": element.ParentLocationGroup.__text, "LgLevel": element.LgLevel.__text, "Child" : [] } formattedList.push(obj); }); var groupDataList = this.groupBy(formattedList, "LgLevel"); var parents = groupDataList[0]; var child = groupDataList[1]; var childOfChild = groupDataList[2]; child.forEach(c => { c.Child = childOfChild.filter(x => x.ParentLocationGroup == c.Id); }) parents.forEach(p => { p.Child = child.filter(x => x.ParentLocationGroup == p.Id); }) this.model = parents; }archivo html
<ul class="nav site-nav"> <li class=flyout> <a href=#>Dropdown</a> <!-- Flyout --> <ul class="flyout-content nav stacked"> <li *ngFor="let parent of model" [class.flyout-alt]="parent.Child.length > 0"><a href=#>{{parent.Name}}</a> <ul *ngIf="parent.Child.length > 0" class="flyout-content nav stacked"> <li *ngFor="let c of parent.Child" [class.flyout-alt]="c.Child.length > 0"><a href=#>{{c.Name}}</a> <ul *ngIf="c.Child.length > 0" class="flyout-content nav stacked"> <li *ngFor="let cc of c.Child" [class.flyout-alt]="cc.Child.length > 0"><a href=#>{{cc.Name}}</a></li> </ul> </li> </ul> </li> </ul> </li> </ul>Según los datos de respuesta de su servidor, organice los datos del modelo. El formato json de respuesta cambió ( __texto a #texto )
var obj = { "Id": element.Id, "Name": element.Name && element.Name.#text ? element.Name.#text : element.Name, "GroupId": element.GroupId && element.GroupId.#text ? element.GroupId.#text : element.GroupId, "ParentLocationGroup": element.ParentLocationGroup && element.ParentLocationGroup.#text ? element.ParentLocationGroup.#text : element.ParentLocationGroup, "LgLevel": element.LgLevel && element.LgLevel.#text ? element.LgLevel.#text : element.LgLevel, "Child" : [] }Parece que ya tiene otra respuesta que cumple con sus requisitos. Pero esta solución me tomó algún tiempo para llegar a. Así que decidí publicarlo de todos modos.
El siguiente fragmento de código se usa para construir la estructura de árbol de los datos jerárquicos padre-hijo:
processData(data) { let locationData = data.ArrayOfLocationGroup.LocationGroup; let level0 = []; let tempMap = {}; for (let i = 0; i < locationData.length; i++) { let currItem = this.getDataObject(locationData[i]); if (tempMap[currItem.id] == undefined) { tempMap[currItem.id] = currItem; if (tempMap[currItem.parentLocationGroup] == undefined) { tempMap[currItem.parentLocationGroup] = { children: [] }; } tempMap[currItem.parentLocationGroup].children.push(currItem); } else { if (tempMap[currItem.id]) { currItem.children = tempMap[currItem.id].children; } tempMap[currItem.id] = currItem; if (tempMap[currItem.parentLocationGroup] == undefined) { tempMap[currItem.parentLocationGroup] = { children: [] }; } tempMap[currItem.parentLocationGroup].children.push(currItem); } if (currItem.lgLevel == "0") { if (level0.indexOf(currItem) == -1) { level0.push(currItem); } } } this.levelData = level0; } Los datos agregados se pasan como entrada a un componente dropdown que los representa como un menú desplegable de varios niveles.
Esta solución supuestamente funcionará para cualquier nivel de niños. El componente dropdown se puede modificar para cambiar la forma en que se representan los datos según sus requisitos.
Tomé el html y css para el menú desplegable multinivel desde aquí:
https://phppot.com/css/multilevel-dropdown-menu-with-pure-css/
El código para cerrar el menú desplegable cuando se hace clic fuera de esta respuesta:
https://stackoverflow.com/a/59234391/9262488
Espero que encuentres esto útil.
¿Por qué no crear un componente de árbol y vincular entradas recursivamente?
La solución propuesta es
O(n) .Primero diseñe el modelo de datos: tiene que ser una estructura de nodo de árbol:
export interface GroupId { /* appropriate members... */ } export interface ParentLocationGroup { /* other members... */ __text: string; } export interface LgLevel { /* other members... */ __text: string; } export interface DataModel { Id: string, Name: string, GroupId: GroupId, ParentLocationGroup: ParentLocationGroup, LgLevel: LgLevel, children: DataModel[] }Luego agregue sus datos en el componente de nivel superior (o incluso mejor, en su servicio de datos; debería poder abstraer eso con bastante facilidad):
// dropdown.component.ts @Component({ selector: 'app-dropdown', template: ` <ul class="nav site-nav"> <li class=flyout> <a href=#>Dropdown</a> <app-dynamic-flyout [data]="data"></app-dynamic-flyout> </li> </ul> ` }) export class DropdownComponent { data: DataModel[]; constructor(dataService: YourDataService){ let data; dataService.getYourData() .pipe(map(d => d.ArrayOfLocationGroup.LocationGroup))) // Make sure every item has the `children` array property initialized // since it does not come with your data .subscribe(d => data = d.map(i => ({...i, children: []}))); // Create a lookup map for building the data tree let idMap = data.reduce((acc, curr) => ({...acc, [curr.Id]: curr}), {}); this.data = data.reduce( (acc, curr) => curr.LgLevel.__text == 0 || !curr.ParentLocationGroup // Either the level is 0 or there is no parent group // (the logic is unclear here - even some of your lvl 0 nodes have a `ParentGroup`) // -> we assume this is a top-level node and put it to the accumulator ? [...acc, curr] // Otherwise push this node to an appropriate `children` array // and return the current accumulator : idMap[curr.ParentLocationGroup.__text].children.push(curr) && acc, [] ); } }Y cree el componente flotante dinámico recurrente:
// dynamic-flyout.component.ts @Component({ selector: 'app-dynamic-flyout', template: ` <ul *ngIf="!!data.length" class="flyout-content nav stacked"> <li *ngFor="let item of data" [class.flyout-alt]="!!item.children.length"> <a href=#>{{item.Name}}</a> <app-dynamic-flyout [data]="item.children"></app-dynamic-flyout> </li> </ul> ` }) export class DynamicFlyoutComponent { @Input() data: DataModel[]; }La solución no está probada, pero lo orientará en la dirección correcta...
Espero que esto ayude un poco :-)