Angular 2 Динамически вставить компонент в определенный узел DOM без использования ViewContainerRef

У меня есть вопрос, касающийся создания динамических компонентов в Angular 2 RC5.

Итак, давайте предположим, что у нас есть два простых угловых компонента:

  @Component({
      template: `
        <div id="container">
          <h1>My Component</h1>
        </div>
       `,
      selector: 'my-app'
  })
  export class AppComponent { }

  @Component({
      template: '<p>{{text}}</p>',
      selector: 'simple-cmp'
  })
  export class SimpleComponent { public text='Hello World!' }

Затем некоторый внешний неугловой кусок кода модифицирует DOM:

let newNode = document.createElement('div');
newNode.id = 'placeholder';
document.getElementById('container').appendChild(newNode);

Вот несколько предположительных деревьев после манипуляций:

 <div id="container">
      <h1>My Component</h1>
      <div id="placeholder"></div>
 </div>

Так что я пытаюсь просто динамически добавить экземпляр SimpleComoponent в #placeholder div. Как мне достичь этого результата?

Я пытался использовать ComponentFactory.createComponent(injector, [], newNode), хотя он добавил компонент, но ни перехватчики жизненного цикла, ни привязка не работали вообще.

Я считаю, что есть какой-то способ реализовать это с помощью ViewContainerRef, но как я могу связать его с динамически созданным узлом?

Вот результат, который я ожидаю

 <div id="container">
      <h1>My Component</h1>
      <div id="placeholder">
        <simple-cmp>Hello world!</simple-cmp>
      </div>
 </div>

Спасибо!

2 ответа

Решение

При создании компонента вы можете передать узел DOM, который будет действовать как элемент хоста созданного компонента:

create (инжектор: инжектор, projectableNodes?: any [] [], rootSelectorOrNode?: string | any, ngModule?: NgModuleRef): ComponentRef

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

Итак, вот что вам нужно сделать:

1) Создайте компонент, определяющий корневой узел, под которым он должен быть добавлен.
2) Присоедините представление к ApplicationRef, чтобы получить обнаружение изменений. У тебя еще не будет Input а также ngOnChanges операции, но обновление DOM будет работать нормально.

  @Component({
      template: `
        <div id="container">
          <h1>My Component</h1>
        </div>
       `,
      selector: 'my-app'
  })
  export class AppComponent { 
      constructor(private resolver: ComponentFactoryResolver,
                  private injector: Injector,
                  private app: ApplicationRef) { 

      }

      addDynamicComponent() {
         let factory = this.resolver.resolveComponentFactory(SimpleComponent);

         let newNode = document.createElement('div');
         newNode.id = 'placeholder';
         document.getElementById('container').appendChild(newNode);

         const ref = factory.create(this.injector, [], newNode);
         this.app.attachView(ref.hostView);
      }
  }

Вы никогда не должны изменять DOM вне Angular, потому что это приведет к непредсказуемому поведению. Даже если вы добавляете <simple-cmp> Элемент вручную, это ничего не значит, потому что он не обрабатывается Angular. Все изменения в DOM внутри приложения Angular должны проходить через методы Angular.

Динамически создайте новый компонент:

@Component({
    selector: 'my-component',
    template: '<div #element></div>',
})
export class MyComponent {
    @ViewChild('element', {read: ViewContainerRef}) private anchor: ViewContainerRef;

    constructor(private resolver: ComponentFactoryResolver) { }

    whatever() {
        let factory = this.resolver.resolveComponentFactory(ChildComponent);
        this.anchor.createComponent(factory);
    }
}
Другие вопросы по тегам