Как создать многоуровневый выпадающий список и связать его с данными, поступающими с сервера - PullRequest
15 голосов
/ 20 февраля 2020

Я очень новичок в Angular. У меня есть работа на Angular.

Мне нужно связать Вложенный раскрывающийся список для Json данных, поступающих с сервера, путем вызова Rest Api.

Данные имеют один атрибут LgLevel, Определяет уровень в иерархии группа. Родитель будет иметь level=0, Немедленный Child=1, Grandchild=2 и так далее. Child и Grandchild имеет поле ParentLocationGroup, которое показывает, в каком родительском меню будет находиться дочернее меню.

Это мои json данные. У меня огромные данные, но я не показываю все.

{
"ArrayOfLocationGroup": {
  "LocationGroup": [
     {
        "Id": "628",
        "Name": "TEST1",
        "GroupId": {
           "_xmlns": "http://www.air-watch.com/servicemodel/resources"
        },
        "ParentLocationGroup": {
           "_uuid": "bdce4396-9c60-4831-90f2-6f793becb362",
           "__text": "570"
        },
        "LgLevel": {
           "_xmlns": "http://www.air-watch.com/servicemodel/resources",
           "__text": "0"
        }
     },
     {
        "Id": "630",
        "Name": "TEST2",
        "GroupId": {
           "_xmlns": "http://www.air-watch.com/servicemodel/resources",
           "__text": "PAM-TEST"
        },
        "ParentLocationGroup": {
           "_uuid": "894055b6-b132-4dc2-a12a-971ecc0c7224",
           "__text": "628"
        },
        "LgLevel": {
           "_xmlns": "http://www.air-watch.com/servicemodel/resources",
           "__text": "1"
        }
     },
     {
        "Id": "631",
        "Name": "TEST3",
        "GroupId": {
           "_xmlns": "http://www.air-watch.com/servicemodel/resources",
           "__text": "PAA-TEST"
        },
        "ParentLocationGroup": {
           "_uuid": "894055b6-b132-4dc2-a12a-971ecc0c7224",
           "__text": "628"
        },
        "LgLevel": {
           "_xmlns": "http://www.air-watch.com/servicemodel/resources",
           "__text": "1"
        }
     },
     {
        "Id": "697",
        "Name": {
           "_xmlns": "http://www.air-watch.com/servicemodel/resources",
           "__text": "TEST4"
        },
        "GroupId": {
           "_xmlns": "http://www.air-watch.com/servicemodel/resources",
           "__text": "PAE-TEST"
        },
        "ParentLocationGroup": {
           "_uuid": "894055b6-b132-4dc2-a12a-971ecc0c7224",
           "__text": "628"
        },
        "LgLevel": {
           "_xmlns": "http://www.air-watch.com/servicemodel/resources",
           "__text": "1"
        }
     },
     {
        "Id": "700",
        "Name": "TEST5",
        "GroupId": {
           "_xmlns": "http://www.air-watch.com/servicemodel/resources",
           "__text": "cuba"
        },
        "ParentLocationGroup": {
           "_uuid": "704af4cf-9feb-4f1b-aa00-d1c7926f7901",
           "__text": "694"
        },
        "LgLevel": {
           "_xmlns": "http://www.air-watch.com/servicemodel/resources",
           "__text": "2"
        }
     },
     {
        "Id": "706",
        "Name": "TEST5",
        "GroupId": {
           "_xmlns": "http://www.air-watch.com/servicemodel/resources",
           "__text": "VOIP-Test"
        },
        "ParentLocationGroup": {
           "_uuid": "894055b6-b132-4dc2-a12a-971ecc0c7224",
           "__text": "628"
        },
        "LgLevel": {
           "_xmlns": "http://www.air-watch.com/servicemodel/resources",
           "__text": "1"
        }
     },
     {
        "Id": "718",
        "Name": "TEST7",
        "GroupId": {
           "_xmlns": "http://www.air-watch.com/servicemodel/resources"
        },
        "ParentLocationGroup": {
           "_uuid": "894055b6-b132-4dc2-a12a-971ecc0c7224",
           "__text": "628"
        },
        "LgLevel": {
           "_xmlns": "http://www.air-watch.com/servicemodel/resources",
           "__text": "1"
        }
     },
     {
        "Id": "719",
        "Name": "TEST8",
        "GroupId": {
           "_xmlns": "http://www.air-watch.com/servicemodel/resources",
           "__text": "MEM_RS"
        },
        "ParentLocationGroup": {
           "_uuid": "52073e2b-48b5-41a9-9c2b-d793835cf285",
           "__text": "718"
        },
        "LgLevel": {
           "_xmlns": "http://www.air-watch.com/servicemodel/resources",
           "__text": "2"
        }
     },
     {
        "Id": "752",
        "Name": "TEST9",
        "GroupId": {
           "_xmlns": "http://www.air-watch.com/servicemodel/resources",
           "__text": "ELDIT"
        },
        "ParentLocationGroup": {
           "_uuid": "894055b6-b132-4dc2-a12a-971ecc0c7224",
           "__text": "628"
        },
        "LgLevel": {
           "_xmlns": "http://www.air-watch.com/servicemodel/resources",
           "__text": "1"
        }
     },
     {
        "Id": "753",
        "Name": "TEST10",
        "GroupId": {
           "_xmlns": "http://www.air-watch.com/servicemodel/resources",
           "__text": "GXYA"
        },
        "ParentLocationGroup": {
           "_uuid": "52073e2b-48b5-41a9-9c2b-d793835cf285",
           "__text": "718"
        },
        "LgLevel": {
           "_xmlns": "http://www.air-watch.com/servicemodel/resources",
           "__text": "2"
        }
     },
     {
        "Id": "760",
        "Name": "TEST11",
        "GroupId": {
           "_xmlns": "http://www.air-watch.com/servicemodel/resources",
           "__text": "STAGE2"
        },
        "ParentLocationGroup": {
           "_uuid": "894055b6-b132-4dc2-a12a-971ecc0c7224",
           "__text": "628"
        },
        "LgLevel": {
           "_xmlns": "http://www.air-watch.com/servicemodel/resources",
           "__text": "1"
        }
     },
     {
        "Id": "761",
        "Name": "TEST12",
        "GroupId": {
           "_xmlns": "http://www.air-watch.com/servicemodel/resources",
           "__text": "INIT"
        },
        "ParentLocationGroup": {
           "_uuid": "894055b6-b132-4dc2-a12a-971ecc0c7224",
           "__text": "628"
        },
        "LgLevel": {
           "_xmlns": "http://www.air-watch.com/servicemodel/resources",
           "__text": "1"
        }
     },
     {
        "Id": "762",
        "Name": "TEST13",
        "GroupId": {
           "_xmlns": "http://www.air-watch.com/servicemodel/resources",
           "__text": "USIT"
        },
        "ParentLocationGroup": {
           "_uuid": "894055b6-b132-4dc2-a12a-971ecc0c7224",
           "__text": "628"
        },
        "LgLevel": {
           "_xmlns": "http://www.air-watch.com/servicemodel/resources",
           "__text": "1"
        }
     }
  ],
  "_xmlns:xsd": "http://www.w3.org/2001/XMLSchema"
}
}

Я пытался его разработать, но я нашел все примеры bootstrap с данными о состоянии c в файле html и отдельном файле CSS это было сложно для меня.

Я хочу сделать это динамически, используя TypeScript. Как я могу начать работать над этим.

Ответы [ 3 ]

4 голосов
/ 25 февраля 2020

Кажется, у вас уже есть другой ответ, который отвечает вашим требованиям. Но это решение заняло у меня некоторое время, чтобы придумать. Поэтому решил опубликовать его в любом случае.

Приведенный ниже фрагмент кода используется для построения древовидной структуры иерархических данных родитель-потомок:

  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;
  }

Агрегированные данные передаются в качестве входных данных для компонента dropdown, который отображает это как многоуровневое выпадающее меню.

Это решение предположительно будет работать для любого уровня детей. Компонент dropdown можно изменить, чтобы изменить способ отображения данных в соответствии с вашими требованиями.

Я выбрал html и css для многоуровневого раскрывающегося меню здесь: https://phppot.com/css/multilevel-dropdown-menu-with-pure-css/ Код для закрытия выпадающего меню при нажатии за пределами этого ответа: { ссылка }

Надеюсь, вы найдете это полезным.

4 голосов
/ 24 февраля 2020

Это образец кода, который вам нужен согласно данным вложенного уровня из ваших данных json. Теперь вы можете для l oop отформатированных данных json в DOM, используя данные модель . Я надеюсь, что это поможет вам создать многоуровневый раскрывающийся список

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;
}

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>

В соответствии с данными ответа вашего сервера организуйте данные модели. Ответ json формат изменен ( __ текст на # текст )

 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" : []
  }
1 голос
/ 26 февраля 2020

Почему бы не создать древовидный компонент и рекурсивно привязать входы к нему?

Предлагаемое решение:

  • Глубина-агность c - оно будет работать для любого количества уровней в вашем дереве данных (даже если оно изменяется ad-ho c)
  • довольно эффективно - оно объединяет ваши данные в O(n).

Сначала спроектируйте данные модель - это должна быть древовидная структура:

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[]
}

Затем агрегируйте ваши данные в компоненте верхнего уровня (или даже лучше - в вашей службе данных; вы должны достаточно легко абстрагироваться). ):

// 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, 
      []
    );
  }
}

И создать рекуррентный динамический c всплывающий компонент:

// 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[];
}

Решение не проверено, но оно направит вас в правильном направлении ...

Надеюсь, это немного поможет: -)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...