这是因为您正在覆盖您的用户列表,而不是添加新用户。一个非常简单的解决方案是向用户添加一个“儿童”字段,然后执行类似的操作
<ul>
<li *ngFor="let user of list">
<button (click)="onClick(user)"> {{user.name}}</button>
<ul *ngIf="user.children != null">
<li *ngFor="let child of user.children">{{child.name}}</li>
</ul>
</li>
</ul>
onClick 将加载的用户添加到子字段的位置
onClick(user) {
this.dataService.getUsers(user.id).subscribe(x => user.children = x);
}
如果您还添加一个“扩展”字段,然后基于该字段展开/折叠子节点,您将拥有一个简单的功能树。
这是一个非常简单的解决方案,但并不理想。您使用 mat-tree 的想法很好,但它非常复杂。
你需要定义你的树节点
export class StackOverflowNode {
constructor(public item: User, public level = 1, public expandable = false,
public isLoading = false) {}
}
定义您的数据源
export class StackOverflowDataSource implements DataSource<StackOverflowNode> {
dataChange = new BehaviorSubject<StackOverflowNode[]>([]);
get data(): StackOverflowNode[] { return this.dataChange.value; }
set data(value: StackOverflowNode[]) {
this._treeControl.dataNodes = value;
this.dataChange.next(value);
}
constructor(private _treeControl: FlatTreeControl<StackOverflowNode>,
private _service: UserService) {}
connect(collectionViewer: CollectionViewer): Observable<StackOverflowNode[]> {
this._treeControl.expansionModel.changed.subscribe(change => {
if ((change as SelectionChange<StackOverflowNode>).added ||
(change as SelectionChange<StackOverflowNode>).removed) {
this.handleTreeControl(change as SelectionChange<StackOverflowNode>);
}
});
return merge(collectionViewer.viewChange, this.dataChange).pipe(map(() => this.data));
}
disconnect(collectionViewer: CollectionViewer): void {}
/** Handle expand/collapse behaviors */
handleTreeControl(change: SelectionChange<StackOverflowNode>) {
if (change.added) {
change.added.forEach(node => this.toggleNode(node, true));
}
if (change.removed) {
change.removed.slice().reverse().forEach(node => this.toggleNode(node, false));
}
}
/**
* Toggle the node, remove from display list
*/
toggleNode(node: StackOverflowNode, expand: boolean) {
node.isLoading = true;
this._service.getUsers(node.item.id).subscribe(children => {
const index = this.data.indexOf(node);
if (!children || index < 0) { // If no children, or cannot find the node, no op
return;
}
if (expand) {
const nodes = children.map(child =>
new StackOverflowNode(child, node.level + 1, false));
this.data.splice(index + 1, 0, ...nodes);
} else {
let count = 0;
for (let i = index + 1; i < this.data.length
&& this.data[i].level > node.level; i++, count++) {}
this.data.splice(index + 1, count);
}
// notify the change
this.dataChange.next(this.data);
node.isLoading = false;
});
}
并在您的组件中使用它
@Component({
selector: 'app-sidebar',
templateUrl: './sidebar.component.html',
styleUrls: ['./sidebar.component.scss']
})
export class SidebarComponent {
treeControl: FlatTreeControl<StackOverflowNode>;
dataSource: StackOverflowDataSource;
getLevel = (node: StackOverflowNode) => node.level;
isExpandable = (node: StackOverflowNode) => node.expandable;
hasChild = (_: number, _nodeData: StackOverflowNode) => _nodeData.expandable
constructor(private dataService: DataService) {
this.treeControl = new FlatTreeControl<StackOverflowNode>(this.getLevel, this.isExpandable);
this.dataSource = new StackOverflowDataSource(this.treeControl, this.userService);
this.dataService.getUsers().subscribe(x => this.dataSource.data = x.map(data => new StackOverflowNode(data,0,true,false)))
}
}
并在您的 html 中使用它
<mat-tree [dataSource]="dataSource" [treeControl]="treeControl">
<mat-tree-node *matTreeNodeDef="let node" matTreeNodePadding>
<button mat-icon-button disabled></button>
{{node.item.name}}
</mat-tree-node>
<mat-tree-node *matTreeNodeDef="let node; when: hasChild" matTreeNodePadding>
<button mat-icon-button
[attr.aria-label]="'toggle ' + node.filename" matTreeNodeToggle>
<mat-icon class="mat-icon-rtl-mirror">
{{treeControl.isExpanded(node) ? 'expand_more' : 'chevron_right'}}
</mat-icon>
</button>
{{node.item.name}}
<mat-progress-bar *ngIf="node.isLoading"
mode="indeterminate"
class="example-tree-progress-bar"></mat-progress-bar>
</mat-tree-node>
</mat-tree>
这已针对来自https://material.angular.io/components/tree/examples的数据源进行了调整。您可以研究该页面以了解此代码的工作原理。请注意,为了简单起见,我假设您的列表只有 2 个级别 - 而不是实际检查一个节点是否有子节点,我自动假设 0 级节点有而 1 级节点没有。