Angular - Injectable dynamic component?

I have working code that injects any component through a service in HTML:

ModalWindow.ts:

@Component({
  selector: 'modal-window'
  template: `
    <div class="modal-dialog" role="document">
        <div class="modal-content"><ng-content></ng-content></div>
    </div>
  `
})
export class ModalWindow {
}

Modalcontent.ts:

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

ModalService.ts:

/*1*/   @Injectable()
/*2*/   export class ModalService {
/*3*/     
/*4*/     constructor(private _appRef: ApplicationRef, private _cfr: ComponentFactoryResolver, private _injector: Injector) {
/*5*/     }
/*6*/     
/*7*/     open(content: any) {
/*8*/       const contentCmpFactory = this._cfr.resolveComponentFactory(content);
/*9*/       const windowCmpFactory = this._cfr.resolveComponentFactory(ModalWindow); 
/*10*/       
/*11*/       const contentCmpt = contentCmpFactory.create(this._injector);
/*12*/       const windowCmpt = windowCmpFactory.create(this._injector, [[contentCmpt.location.nativeElement]]);
/*13*/       
/*14*/       document.querySelector('body').appendChild(windowCmpt.location.nativeElement);
/*15*/       
/*16*/       this._appRef.attachView(contentCmpt.hostView);
/*17*/       this._appRef.attachView(windowCmpt.hostView);
/*18*/     }
/*19*/   }

App.ts:

@Component({
  selector: 'my-app',
  template: `
    <button (click)="open()">Open modal window</button>
  `,
})

Result (when you click the button that calls this method):

enter image description here

I already know that contentCmpFactoryand windowCmpFactory(lines # 8.9)

But I do not know what will happen next. As for lines No. 11, No. 12 - the documents say that "creates a new component."

Questions:

1 - line # 12: What does [[contentCmpt.location.nativeElement]]? (documents say its type projectableNodes?: any[][]- What do they mean?)

2 - line # 14: What does [[windowCmpt.location.nativeElement]]?

3 - line # 16, # 17: why and why do I need them, if I have already done appendChild? (docs says: attaches the view so that it is dirty. - right?).

Plunker

+1
1

:

1) Angular ComponentFactory

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

1.1 - , Angular

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

. .

enter image description here

. doc

1.2 node, "" () ng-content, .

-, ng-content node.

@Component({
  selector: 'modal-window',
  template: `
    <div class="modal-dialog">
      <div class="modal-content">
        <ng-content></ng-content> // <== place for projection
      </div>
    </div>
  ` 
})
export class ModalWindow {

:

<modal-window>
  <modal-content></modal-content>
  <div>Some other content</div>
</modal-window>

, :

<modal-window>
  <div class="modal-dialog">
    <div class="modal-content">
       <modal-content></modal-content> // our projectable nodes
       <div>Some other content</div>   // replaced ng-content
    </div>
  </div>
</modal-window>

,

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

, .

(contentCmpt.location) contentCmpt. modal-content. Angular , ng-content.

div

<modal-window>
  <modal-content></modal-content>
  <div>Some other content</div> <== here
</modal-window>

, :

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

projectableNodes [] []?

2)

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

modal-window. ComponentRef , location getter

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

document.body . .

3). , ModalContent , .

@Component({
  selector: 'modal-content',
  template: `
    I'm beeing opened as modal! {{ counter }}
    <button (click)="counter = counter + 1">Increment</button>
  `
})
export class ModalContent {
  counter = 1;
}

 this._appRef.attachView(contentCmpt.hostView);

, ComponentFactory.create, - ( ViewContainerRef.createComponent). Angular API , root views https://github.com/angular/angular/blob/master/packages/core/src/application_ref.ts#L428, Application.tick https://github.com/angular/angular/blob/master/packages/core/src/application_ref.ts#L558

+4

Source: https://habr.com/ru/post/1678337/


All Articles