Angular-服务注入动态组件? [英] Angular - Service injecting dynamic component?

查看:89
本文介绍了Angular-服务注入动态组件?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个工作代码,可以通过服务将任何组件注入HTML:

I have a working code which injects any component via a service to the 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 click a button which calls this service method ) :

我已经知道contentCmpFactorywindowCmpFactory是什么(#8,9 行)

I already know what contentCmpFactory and windowCmpFactory are (lines #8,9)

但是我不知道以后发生了什么.关于第11行,第12行-文档说创建一个新组件".

But I don't udnerstnad what's going on later. Regarding lines #11,#12 - the docs says "creates a new component".

问题:

1-第12行:[[contentCmpt.location.nativeElement]]的作用是什么? (文档说它的类型是projectableNodes?: any[][] -这是什么意思?)

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

2-第14行:[[windowCmpt.location.nativeElement]]的作用是什么?

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

3-#16,#17行:如果已经做了appendChild,我需要什么以及为什么需要它们? ( docs说:附加视图,以便对其进行脏检查.-这样吗?).

3 - line #16,#17 : what and why do I need them if I already did appendChild ? (docs says : Attaches a view so that it will be dirty checked. - so ?).

PLUNKER

推荐答案

答案:

1) Angular使用ComponentFactory并使用给定的元素注入器和可投影节点数组创建组件实例

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 当angular将

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

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.

为了投影某些东西,我们的组件模板必须包含ng-content节点.

In order to project something our component template has to contain 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 {

我们可以在父组件模板中使用上述组件,如下所示:

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.

我们正在引用(contentCmpt.location)创建的早期contentCmpt组件的宿主元素.这是modal-content元素.然后,Angular会尽一切力量将其投射到ng-content位置.

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.

在上面的示例中,我添加了一个div

In example above i added one 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]]);

结论为什么projectableNodes是any [] []?

2)在下一行中

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

我们正在引用在内存modal-window元素中创建的内容. ComponentRef允许我们执行此操作,因为它在location getter

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<C> {
  /**
   * Location of the Host Element of this Component Instance.
   */
  abstract get location(): ElementRef;

,然后将其作为最后一个孩子插入document.body标记中.这样我们就可以在页面上看到它.

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

3)假设我们的ModalContent不仅具有静态内容,还将执行一些交互操作.

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 }}
    <button (click)="counter = counter + 1">Increment</button>
  `
})
export class ModalContent {
  counter = 1;
}

如果我们删除

 this._appRef.attachView(contentCmpt.hostView);

然后,由于我们是通过ComponentFactory.create创建的,并且我们的视图不属于更改检测树中的任何项目,因此我们的视图将不会在更改检测周期中进行更新(与通过ViewContainerRef.createComponent创建的视图不同). 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

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

这篇关于Angular-服务注入动态组件?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆