简体   繁体   English

Angular-服务注入动态组件?

[英]Angular - Service injecting dynamic component?

I have a working code which injects any component via a service to the HTML: 我有一个工作代码,该代码通过服务将任何组件注入HTML:

ModalWindow.ts: 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 : Modalcontent.ts:

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

ModalService.ts : 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 click a button which calls this service method ) : 结果(单击一个调用此服务方法的按钮时):

在此处输入图片说明

I already know what contentCmpFactory and windowCmpFactory are ( lines #8,9 ) 我已经知道contentCmpFactorywindowCmpFactory是什么( 第#8,9行

But I don't udnerstnad what's going on later. 但是我不知道以后会发生什么。 Regarding lines #11,#12 - the docs says "creates a new component". 关于第11行,第12行-文档说“创建一个新组件”。

Questions : 问题:

1 - line #12 : What does [[contentCmpt.location.nativeElement]] do ? 1行#12: [[contentCmpt.location.nativeElement]]做什么? ( the docs says its type is projectableNodes?: any[][] - What do they mean ??) 文档说它的类型是projectableNodes?: any[][] -它们是什么意思?)

2 - line #14 : What does [[windowCmpt.location.nativeElement]] do ? 2行14号: [[windowCmpt.location.nativeElement]]做什么?

3 - line #16,#17 : what and why do I need them if I already did appendChild ? 3行#16,#17:如果我已经执行appendChild ,为什么又需要它们? ( docs says : Attaches a view so that it will be dirty checked. - so ?). docs说:附加视图,以便对其进行脏检查。-这样吗?)。

PLUNKER 朋克

Answers: 答案:

1) Angular takes ComponentFactory and create component instance with given element injector and with array of projectable nodes 1) Angular使用ComponentFactory并使用给定的元素注入器和可投影节点数组创建组件实例

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

1.1 Element Injector will be used when angular will resolve dependency 1.1当角度将解决依赖关系时将使用元素注入器

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. 1.2可投影节点是节点元素,它们在组件模板的ng-content中被“投影”(包含)。

In order to project something our component template has to contain ng-content node. 为了投射某些东西,我们的组件模板必须包含ng-content节点。

@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 {

We can use component above in parent component template as follows: 我们可以在父组件模板中使用上述组件,如下所示:

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

So our final result will look like: 因此,我们的最终结果将如下所示:

<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>

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. 我们正在获取创建的早期contentCmpt组件的host元素的引用( contentCmpt.location )。 This is modal-content element. 这是modal-content元素。 And then angular will do all magic to project it in ng-content place. 然后,Angular将尽一切魔力将其投射到ng-content位置。

In example above i added one div 在上面的示例中,我添加了一个div

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

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[][]? 结论为什么projectableNodes是any [] []?

2) During the next line 2)在下一行

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

we're getting reference to created in memory modal-window element. 我们正在引用在内存中创建的modal-window元素。 ComponentRef allows us to do this because it stores reference to the host element in location getter ComponentRef允许我们执行此操作,因为它在location getter中存储了对宿主元素的引用

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

and then inseting it in document.body tag as last child. 然后将它作为最后一个孩子插入document.body标记中。 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. 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;
}

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 ). 那么我们的视图将不会在更改检测周期中进行更新,因为我们是通过ComponentFactory.create创建的,并且该视图不属于更改检测树中任何项目的一部分(与通过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 Angular为此目的打开了API,我们可以轻松地将视图添加到根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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM