Angular 2在不使用ViewContainerRef的情况下将组件动态插入特定DOM节点

时间:2016-09-06 20:20:19

标签: angular typescript angular2-template

我对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 个答案:

答案 0 :(得分:14)

When creating a component您可以传递将充当所创建组件的主机元素的DOM节点:

  

create(injector:Injector,projectableNodes?:any [] [],    rootSelectorOrNode?:string | any ,ngModule?:NgModuleRef):   ComponentRef

但由于此组件不是任何其他组件的子组件,因此您必须手动将其附加到ApplicationRef,以便进行更改检测。

所以这就是你需要做的事情:

1)创建一个组件,指定应添加它的根节点 2)将视图附加到ApplicationRef,以便进行更改检测。您仍然没有InputngOnChanges操作,但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);
      }
  }

答案 1 :(得分:7)

你永远不应该在Angular之外修改DOM,因为它会导致不可预测的行为。即使您手动附加<simple-cmp>元素,它也没有任何意义,因为它没有被Angular处理。 Angular app中对DOM的所有更改都必须通过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);
    }
}