В моей реализации поддержки вложенного mat-tree с динамическими данными я вижу, что данные загружаются при развертывании элемента дерева, но дочерние элементы никогда не отображаются.
Я взял пример кода из документации Angular Material и попытался настроить его под свои нужды. К сожалению, я не могу сказать, что полностью понимаю, что там происходит, и поэтому я застрял в устранении проблемы.
Основной компонент:
export class TrackableBrowserComponent {
nestedTreeControl: NestedTreeControl<CategoryNode>;
nestedDataSource: CategoryDataSource;
hasNestedChild = (_: number, node: CategoryNode) => true;
getChildren = (node: CategoryNode) => node.children;
constructor(categoryService: CategoryService) {
this.nestedTreeControl = new NestedTreeControl<CategoryNode>(this.getChildren);
this.nestedDataSource = new CategoryDataSource(categoryService, this.nestedTreeControl);
}
}
Мат-дерево выглядит так:
<mat-tree [dataSource]="nestedDataSource" [treeControl]="nestedTreeControl" class="example-tree">
<mat-tree-node *matTreeNodeDef="let node" matTreeNodeToggle>
<li class="mat-tree-node">
<button mat-icon-button disabled></button>
{{node.name}}: {{node.type}}
</li>
</mat-tree-node>
<mat-nested-tree-node *matTreeNodeDef="let node; when: hasNestedChild">
<li>
<div class="mat-tree-node">
<button mat-icon-button matTreeNodeToggle>
<mat-icon class="mat-icon-rtl-mirror">{{nestedTreeControl.isExpanded(node) ? 'expand_more' : 'chevron_right'}}</mat-icon>
</button>
{{node.name}}
</div>
<ul [class.example-tree-invisible]="!nestedTreeControl.isExpanded(node)">
<ng-container matTreeNodeOutlet></ng-container>
</ul>
</li>
</mat-nested-tree-node>
</mat-tree>
А вот та часть, которая, вероятно, содержит проблему.
export class CategoryNode {
name: string;
description: string;
children: CategoryNode[] = [];
category: Category;
static from(category: Category): CategoryNode {
const n = new CategoryNode();
n.name = category.name;
n.category = category;
n.children = [];
return n;
}
}
export class CategoryDataSource implements DataSource<CategoryNode> {
dataChange: BehaviorSubject<CategoryNode[]> = new BehaviorSubject<CategoryNode[]>([]);
constructor(
private categoryService: CategoryService,
private treeControl: NestedTreeControl<CategoryNode>
) {
this.categoryService.getAll().pipe(
map(categories => categories.map(c => CategoryNode.from(c)))
).subscribe(nodes => this.data = nodes);
}
connect(collectionViewer: CollectionViewer): Observable<CategoryNode[]> {
console.log('collect(): called');
this.treeControl.expansionModel.changed.subscribe(change => {
console.log('collect(): received change event from treeControl.expansionModel');
if ((change as SelectionChange<CategoryNode>).added ||
(change as SelectionChange<CategoryNode>).removed) {
this.handleTreeControl(change as SelectionChange<CategoryNode>);
}
});
return merge(collectionViewer.viewChange, this.dataChange).pipe(map(() => this.data));
}
disconnect(collectionViewer: CollectionViewer): void {
throw new Error('Method not implemented.');
}
get data(): CategoryNode[] {
return this.dataChange.value;
}
set data(value: CategoryNode[]) {
this.treeControl.dataNodes = value;
this.dataChange.next(value);
}
handleTreeControl(change: SelectionChange<CategoryNode>) {
if (change.added) {
console.log('handleTreeControl(): added');
change.added.forEach(node => this.toggleNode(node, true));
} else if (change.removed) {
console.log('handleTreeControl(): removed');
change.removed.slice().reverse().forEach(node => this.toggleNode(node, false));
} else {
console.log('handleTreeControl(): other');
}
}
toggleNode(node: CategoryNode, expand: boolean) {
console.log('toggleNode(): called');
if (expand === true) {
console.log('toggleNode(): expanding node ' + node.name);
node.category.children.pipe(map(all => all.map(item => CategoryNode.from(item)))).subscribe(nodes => {
console.log('toggleNode(): received data ' + JSON.stringify(nodes));
node.children = nodes;
this.treeControl.dataNodes = this.data;
this.dataChange.next(this.data);
});
}
}
}
Мне нужно добавить, что моя сущность Category
имеет объект ReplaySub с именем «children», подписка на который будет запускать вызов GET / cartegories / ID / children для внутреннего API.
export class Category extends BaseEntity<CategoryLinks> {
name: string;
description: string;
parent: ReplaySubject<Category>;
children: ReplaySubject<Category[]>;
}
Моя общая идея состояла в том, чтобы деформировать этот Category
в CategoryNode
и скопировать результат этого ReplaySubject в свойство "children" CategoryNode
, когда элемент развернут. Эта часть, кажется, работает, так как я вижу вывод
console.log('toggleNode(): received data ' + JSON.stringify(nodes));
в журнале браузера.
Я подумал, что тот, кто подключается к этому источнику данных, теперь должен быть уведомлен об этом изменении и соответствующим образом обновлен. Но этого не происходит. Я нажимаю кнопку расширения в дереве, внутренний вызов запускается, но в интерфейсе ничего не происходит.
Есть идеи?