如何创建下拉列表并与来自服务器的数据绑定



我对Angular很陌生。我有一些关于Angular的工作。

我需要通过调用 Rest API 为来自服务器Json数据绑定嵌套下拉列表。

数据有一个属性 级别,指定组层次结构中的级别。父母 将有立即Child=1Grandchild=2等。ChildGrandchild具有组字段,该字段显示在哪个父菜单中,子菜单将在那里。

我尝试过开发它,但我发现所有bootstrap示例html文件中的静态数据和单独的CSS文件中,这对我来说很复杂。

我想使用TypeScript动态执行此操作。我怎样才能开始工作。

这是一个示例编码,您需要根据 json 数据中的嵌套级别数据进行编码。现在,您可以使用模型数据在 DOM 中循环格式化的 json 数据。 我希望这能帮助您创建一个多级下拉列表

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

网页文件

<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 格式已更改(__text更改为#text)

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

看来您已经有了另一个符合您要求的答案。但是这个解决方案花了我一些时间才想出。所以决定无论如何都要发布它。

下面的代码片段用于构造父子分层数据的树状结构:

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组件以根据您的要求更改数据的呈现方式。

我从这里获取了多级下拉菜单的htmlcss
https://phppot.com/css/multilevel-dropdown-menu-with-pure-css/
从此答案外部单击时关闭菜单下拉列表的代码:
https://stackoverflow.com/a/59234391/9262488

希望您觉得这有用。

为什么不创建一个树组件并以递归方式将输入绑定到它?

建议的解决方案是

  • 与深度无关 - 它将适用于数据树中任意数量的级别(即使它临时更改)
  • 非常高效 - 它将您的数据聚合为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, 
[]
);
}
}

并创建循环动态浮出控件组件:

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

该解决方案未经测试,但它会为您指明正确的方向......

希望这有所帮助:-)

最新更新