我一直在尝试动态创建一个组件并将其附加到文档标记.我一直很难搞清楚如何选择正文的ViewContainterRef,所以我可以使用ComponentFactoryResolver添加一个新组件.
我尝试使用下面的代码获取对body容器的引用,但它不起作用.有谁知道怎么做?谢谢!
import {
Component,
ComponentRef,
ApplicationRef,
Injector,
Input,
ViewContainerRef,
ComponentFactoryResolver,
ViewChild,
OnInit,
OnDestroy
} from '@angular/core';
import {
ModalComponent
} from './modal.component';
@Component({
selector: 'my-modal'
})
export class MyModalComponent {
private _bodyRef: ViewContainerRef;
constructor(private resolver: ComponentFactoryResolver, private app: ApplicationRef) {
// Does not work!
this._bodyRef = app['_rootComponents'][0]['_hostElement'].vcRef;
}
ngOnInit() {
// Calls the factory to crate a brand new instance
let componentFactory = this.resolver.resolveComponentFactory(ModalComponent);
this._bodyRef.createComponent(componentFactory);
}
}
Run Code Online (Sandbox Code Playgroud)
Angular2材料团队正在为工具提示和其他动态组件做类似的事情,视图可能在当前容器引用之外.
这是类:https://github.com/angular/material2/blob/master/src/lib/core/portal/dom-portal-host.ts 他们在做什么:
首先注入一堆有用的类:
constructor(
private _viewRef: ViewContainerRef,
private _hostDomElement: Element,
private _componentFactoryResolver: ComponentFactoryResolver,
private _appRef: ApplicationRef,
private _defaultInjector: Injector) {
}
Run Code Online (Sandbox Code Playgroud)
然后:在当前viewContainerRef中创建组件,在您的情况下是您的模态.
ngOnInit() {
// Calls the factory to crate a brand new instance
let componentFactory = this._componentFactoryResolver.resolveComponentFactory(ModalComponent);
let componentRef = this._viewRef.createComponent(componentFactory);
}
Run Code Online (Sandbox Code Playgroud)
然后将其附加到appRef
(this._appRef as any).attachView(componentRef.hostView);
this.setDisposeFn(() => {
(this._appRef as any).detachView(componentRef.hostView);
componentRef.destroy();
});
Run Code Online (Sandbox Code Playgroud)
我从来没有这样做过,所以你可能需要付出一些努力,但我认为这就是方法.
我用单独的服务解决了这个问题:
import {
Injectable,
ComponentFactoryResolver,
ApplicationRef,
Injector,
EmbeddedViewRef,
ComponentRef
} from '@angular/core';
@Injectable()
export class DOMService {
constructor(
private componentFactoryResolver: ComponentFactoryResolver,
private applicationRef: ApplicationRef,
private injector: Injector,
) {}
appendComponentToBody(component: any) {
//create a component reference
const componentRef = this.componentFactoryResolver.resolveComponentFactory(component)
.create(this.injector);
// attach component to the appRef so that so that it will be dirty checked.
this.applicationRef.attachView(componentRef.hostView);
// get DOM element from component
const domElem = (componentRef.hostView as EmbeddedViewRef < any > )
.rootNodes[0] as HTMLElement;
document.body.appendChild(domElem);
return componentRef;
}
removeComponentFromBody(componentRef: ComponentRef < any > ) {
this.applicationRef.detachView(componentRef.hostView);
componentRef.destroy();
}
}Run Code Online (Sandbox Code Playgroud)
然后在您的组件中:
import {
Component,
AfterContentInit
} from '@angular/core';
import {
ComponentToInject
} from 'path/to/component';
import {
DOMService
} from 'path/to/service';
@Component({
selector: 'my-component',
template: '....'
})
export class MyComponent implements AfterContentInit {
constructor(
private DOMService: DOMService,
) {}
ngAfterContentInit() {
// to prevent ExpressionChangedAfterItHasBeenCheckedError
setTimeout(() => {
const cmp = this.DOMService.appendComponentToBody(ComponentToInject);
// if you need to get access to input of injected component. Let's say ComponentToInject has public property title
const instance = cmp.instance;
instance.title = 'Some title';
// if you need to get access to output of injected component. Let's say ComponentToInject assings EventEmitter to onClick property
instance.onClick.subscribe(() => { // do somethis })
});
}
}Run Code Online (Sandbox Code Playgroud)