Использовать компонент в себе рекурсивно для создания дерева

Знаете ли вы, что можно использовать компонент сам по себе? Если да, где читать об этом?

У меня следующая ситуация: есть список основных элементов, каждый основной элемент имеет subItem (то же самое похоже на mainItem), каждый subItem может иметь свой собственный subItem и т.д. Так что лучше использовать вложенность, но как?

Ответы

Ответ 1

Обновление

forwardRef() больше не требуется, потому что directives был перенесен на NgModule.declarations, и поэтому рекурсивные компоненты не должны быть зарегистрированы сами по себе как directives.

Angular 4.x.x Пример плунжера

оригинальный

Это поддерживается. Вам просто нужно добавить компонент в directives: [] в его декоратор @Component(). Поскольку декоратор приходит перед классом, и классы не могут быть указаны, прежде чем они будут объявлены forwardRef().

import {Component, forwardRef, Input} from '@angular/core'

@Component({
  selector: 'tree-node',
  template: `
  <div>{{node.name}}</div>
  <ul>
    <li *ngFor="let node of node.children">
      <tree-node  [node]="node"></tree-node>
    </li>
  </ul>
`
})
export class TreeNode {
  @Input() node;
}
  
@Component({
  selector: 'my-app',
  template: `
    <div>
      <h2>Hello {{name}}</h2>
      <tree-node [node]="node"></tree-node>
    </div>
  `,
  directives: [TreeNode]
})
export class App {
  constructor() {
    this.name = 'Angular2 (Release Candidate!)'
  }

  node = {name: 'root', children: [
    {name: 'a', children: []},
    {name: 'b', children: []},
    {name: 'c', children: [
      {name: 'd', children: []},
      {name: 'e', children: []},
      {name: 'f', children: []},
     ]},
  ]};  
}

Angular 2.0.0-beta.x Пример плунжера

См. также Вставить родительский компонент того же типа, что и дочерний компонент

Ответ 2

Angular 4 пример рекурсивных компонентов: https://plnkr.co/edit/IrW82ye4NKK8cYEPxsFc?p=preview

Выдержка из связанного примера:

//our root app component
import {Component, NgModule, VERSION, Input} from '@angular/core'
import {BrowserModule} from '@angular/platform-browser'

@Component({
  selector: 'tree-node',
  template: '
  <div>{{node.name}}</div>
  <ul>
    <li *ngFor="let node of node.children">
      <tree-node  [node]="node"></tree-node>
    </li>
  </ul>
'
})
export class TreeNode {
  @Input() node;
}

@Component({
  selector: 'my-app',
  providers: [],
  template: '
    <div>
      <h2>Hello {{name}}</h2>
      <tree-node [node]="node"></tree-node>
    </div>
  '
})
export class App {
  constructor() {
    this.name = 'Angular2 (Release Candidate!)'
  }

  node = {name: 'root', children: [
    {name: 'a', children: []},
    {name: 'b', children: []},
    {name: 'c', children: [
      {name: 'd', children: []},
      {name: 'e', children: []},
      {name: 'f', children: []},
     ]},
  ]};  
}

@NgModule({
  imports: [ BrowserModule ],
  declarations: [ App, TreeNode ],
  bootstrap: [ App ]
})
export class AppModule {}

Этот ответ является вики-сообществом, потому что пример ссылки был скопирован из ответа Гюнтера Цохбауэра. Я включил код в тело вопроса, чтобы избежать гниения ссылок.

Ответ 3

Работая с Angular 7, я нашел другие решения для описания ссылки здесь, которые кажутся близкими к ответу Шеннона (я даю эту ссылку, потому что я нашел ее полезной для меня, как для начинающего)

Ответ 4

Использование ng-template является лучшим решением для решения рекурсивных проблем DOM.

@Component({
  selector: 'tree-node',
   template: '
     <ng-container *ngTemplateOutlet="tree;context:{node:node}">
     </ng-container>

     <ng-template #tree let-node="node">
       <div>{{node.name}}</div>
       <ul *ngIf="node.children && node.children.length > 0">
         <ng-container *ngFor="let child of node.children">
           <li>
             <ng-container *ngTemplateOutlet="tree;context:{node:child}">
             </ng-container>
           </li>
         </ng-container>
       </ul>
     </ng-template>
   '
})
export class TreeNode {
    @Input() node;
}

Нет необходимости добавлять компонент в directives: [] в его @Component().

@Component({
  selector: 'my-app',
  template: '
    <div>
      <h2>Hello {{name}}</h2>
      <tree-node [node]="node"></tree-node>
    </div>
  '
})
export class App {

  node = {
    name: 'root', children: [
      { name: 'a', children: [] },
      {
        name: 'b', children: [
          { name: 'b-1', children: [] },
          {
            name: 'b-2', children: [
              { name: 'b-2-1', children: [] },
              { name: 'b-2-2', children: [] },
              { name: 'b-2-3', children: [] }
            ]
          }
        ]
      },
      {
        name: 'c', children: [
          { name: 'c-1', children: [] },
          { name: 'c-2', children: [] }
        ]
      },
    ]
  };

}

Выход:

  • корень
    • б
      • б-1
      • Би 2
        • б-2-1
        • б-2-2
        • б-2-3
    • с
      • с-1
      • с-2

HTML:

<app-tree-node>
    <div>root</div>
    <ul>
        <li>
            <div>a</div>
        </li>
        <li>
            <div>b</div>
            <ul>
                <li>
                    <div>b-1</div>
                </li>
                <li>
                    <div>b-2</div>
                    <ul>
                        <li>
                            <div>b-2-1</div>
                        </li>
                        <li>
                            <div>b-2-2</div>
                        </li>
                        <li>
                            <div>b-2-3</div>
                        </li>
                    </ul>
                </li>
            </ul>
        </li>
        <li>
            <div>c</div>
            <ul>
                <li>
                    <div>c-1</div>
                </li>
                <li>
                    <div>c-2</div>
                </li>
            </ul>
        </li>
    </ul>
</app-tree-node>