typescript - Angular 2 Dynamisk sette inn en komponent i en spesifikk DOM-node uten å bruke ViewContainerRef


Jeg har et spørsmål angående dynamisk komponentoppretting i Angular 2 rc5.

Så la oss anta at vi har to vanlige Angular-komponenter:

  @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!' }

Så noen eksterne ikke-angular biter av kode endrer et DOM:

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

Her er et antatt tre etter manipuleringer:

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

Så det jeg prøver å gjøre er bare å dynamisk legge til en SimpleComponent-instans i #placeholder-diven. Hvordan kan jeg oppnå dette resultatet?

Jeg har prøvd å bruke ComponentFactory.createComponent(injector, [], newNode), det la til komponenten, men verken livssyklus-hooker eller binding fungerte i det hele tatt.

Jeg tror det finnes en måte å implementere dette på ved å bruke ViewContainerRef, men hvordan kan jeg knytte det til en dynamisk opprettet node?

Her er resultatet jeg forventer

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

Takk!

Svar #1

Når du oppretter en komponent, kan du sende inn DOM-noden som skal fungere som et vertselement for den opprettede komponenten:

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

Men siden denne komponenten ikke er et barn av noen annen komponent, må du manuelt feste den til ApplicationRef slik at du får endringsdeteksjon.

Så her er hva du trenger å gjøre:

1) Opprett en komponent som spesifiserer rotnoden hvor den skal legges til.
2) Knytt visningen til ApplicationRef slik at du får endringsdeteksjon. Du vil fortsatt ikke ha Input og ngOnChanges operasjoner, men DOM-oppdateringen vil fungere fint.

  @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);
      }
  }

Svar #2

Du bør aldri endre DOM utenfor Angular fordi det vil føre til uforutsigbar oppførsel. Selv om du legger til <simple-cmp>-elementet manuelt, betyr det ingenting fordi det ikke behandles av Angular. Alle endringer i DOM inne i en Angular-app må gå gjennom Angular-metoder.

Opprett dynamisk en ny komponent:

@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);
    }
}

Relaterte artikler