Angular - Service injecting dynamic component?

后端 未结 1 547
没有蜡笔的小新
没有蜡笔的小新 2021-02-11 08:59

I have a working code which injects any component via a service to the HTML:

ModalWindow.ts:

@Component({
  selector: \'modal-window\'         


        
1条回答
  •  死守一世寂寞
    2021-02-11 10:03

    Answers:

    1) Angular takes ComponentFactory and create component instance with given element injector and with array of projectable nodes

    windowCmpFactory.create(this._injector, [[contentCmpt.location.nativeElement]]);
    

    1.1 Element Injector will be used when angular will resolve dependency

    const value = startView.root.injector.get(depDef.token, NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR);
    

    Here is also simple illustration of dependency resolution algorithm for app without lazy loading. With lazy loading it will look a litte more complicated.

    For more details see design doc element injector vs module injector

    1.2 Projectable nodes are the node elements, which are "projected"(transcluded) in the ng-content that we have in the template of our component.

    In order to project something our component template has to contain ng-content node.

    @Component({
      selector: 'modal-window',
      template: `
        
      ` 
    })
    export class ModalWindow {
    

    We can use component above in parent component template as follows:

    
      
      
    Some other content

    So our final result will look like:

    
      
    
    

    So when we're passing projectable nodes to create method

    windowCmpFactory.create(this._injector, [[contentCmpt.location.nativeElement]]);
    

    we do the same things as described above.

    We'are getting reference (contentCmpt.location) to the host element of created early contentCmpt component. This is modal-content element. And then angular will do all magic to project it in ng-content place.

    In example above i added one div

    
      
      
    Some other content
    <== here

    So the real code should looks like:

    let div = document.createElement('div');
    div.textContent = 'Some other content';
    windowCmpFactory.create(this._injector, [[contentCmpt.location.nativeElement, div]]);
    

    In conclusion Why is projectableNodes an any[][]?

    2) During the next line

    document.querySelector('body').appendChild(windowCmpt.location.nativeElement);
    

    we're getting reference to created in memory modal-window element. ComponentRef allows us to do this because it stores reference to the host element in location getter

    export abstract class ComponentRef {
      /**
       * Location of the Host Element of this Component Instance.
       */
      abstract get location(): ElementRef;
    

    and then inseting it in document.body tag as last child. So we see it on the page.

    3) Let's say our ModalContent has not just static content but will perform some operations for interaction.

    @Component({
      selector: 'modal-content',
      template: `
        I'm beeing opened as modal! {{ counter }}
        
      `
    })
    export class ModalContent {
      counter = 1;
    }
    

    If we remove

     this._appRef.attachView(contentCmpt.hostView);
    

    then our view will not being updated during change detection cycle because we created view via ComponentFactory.create and our view is not part of any item in change detection tree (unlike creation via ViewContainerRef.createComponent). Angular opened API for such purposes and we can easily add view to root views https://github.com/angular/angular/blob/master/packages/core/src/application_ref.ts#L428 and after that our component will be updated during Application.tick https://github.com/angular/angular/blob/master/packages/core/src/application_ref.ts#L558

    0 讨论(0)
提交回复
热议问题