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

Val*_*dov 11 typescript angular2-template angular

我有一个关于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!' }
Run Code Online (Sandbox Code Playgroud)

然后一些外部非角度代码块修改DOM:

let newNode = document.createElement('div');
newNode.id = 'placeholder';
document.getElementById('container').appendChild(newNode);
Run Code Online (Sandbox Code Playgroud)

这是操作后的一些可能的树:

 <div id="container">
      <h1>My Component</h1>
      <div id="placeholder"></div>
 </div>
Run Code Online (Sandbox Code Playgroud)

所以我要做的就是将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>
Run Code Online (Sandbox Code Playgroud)

谢谢!

Max*_*kyi 18

创建组件时,您可以传递将充当所创建组件的主机元素的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);
      }
  }
Run Code Online (Sandbox Code Playgroud)

  • 注意:从 Angular 13.2 开始,ComponentFactory 现已被弃用。我不知道什么是更好的解决方法。 (2认同)
  • 我还想知道如何解决这个问题,因为“ComponentFactory”已被弃用 (2认同)

mar*_*tin 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);
    }
}
Run Code Online (Sandbox Code Playgroud)