Мат-дерево: динамические данные - без обновления пользовательского интерфейса компонента - PullRequest
0 голосов
/ 29 марта 2019

В моей реализации поддержки вложенного 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));

в журнале браузера.

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

Есть идеи?

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