经过几天的挖掘,我终于找到了一个可行的解决方案。
基本上我是通过 http.get 加载模板,添加远程 Head 并在运行时使用 body 生成一个新的动态组件。
现在它可以工作了,我将创建一个从数据库获取 url 的服务并将模板字符串返回给我,因此我只需要执行 1 个请求,我可以将其移至服务。
有一个小问题,远程头附带的样式和脚本在添加到主头后需要一些时间来加载,所以我不得不展示微调器以避免不规则的元素。
export class AppComponent {
@ViewChild('container', {read: ViewContainerRef, static: false})
container: ViewContainerRef;
private componentRef: ComponentRef<{}>;
constructor(private httpStatus: HTTPStatus,
private spinner: NgxSpinnerService,
private http: HttpClient,
private injectorService: InjectorService) {
spinner.show();
http.get('https://somedomain.com/header.php', {responseType: 'text'}).subscribe((header: string) => {
this.injectorService.injectHeader(header);
}, error => {
console.log('error loading header');
}, () => {
http.get('https://somedomain.com/body.html', {responseType: 'text'}).subscribe((body: string) => {
this.injectorService.compileTemplate(this.componentRef, this.container, body);
});
});
}
}
app.component.html
<div #container></div>
injector.service.ts
@Injectable()
export class InjectorService {
constructor(private componentFactoryResolver: ComponentFactoryResolver,
private compiler: Compiler) {}
public injectHeader(includes: string): void {
let head = document.head.innerHTML;
head = head.concat(includes);
document.head.innerHTML = head;
}
public compileTemplate(componentRef: ComponentRef<{}>, container: ViewContainerRef, html: string) {
const metadata = {
selector: 'runtime-sample',
template: html
};
const factory = this.createComponentFactorySync(this.compiler, metadata, DynamicComponent);
if (componentRef) {
componentRef.destroy();
componentRef = null;
}
componentRef = container.createComponent(factory);
}
private createComponentFactorySync(compiler: Compiler, metadata: Component, componentClass: any): ComponentFactory<any> {
const cmpClass = componentClass || DynamicComponent;
const decoratedCmp = Component(metadata)(cmpClass);
@NgModule({ imports: [CommonModule, FormsModule], declarations: [ContentComponent, FilterPipe, decoratedCmp] })
class RuntimeComponentModule { }
const module: ModuleWithComponentFactories<any> = compiler.compileModuleAndAllComponentsSync(RuntimeComponentModule);
return module.componentFactories.find(f => f.componentType === decoratedCmp);
}
}
由于我加载的正文包含一个
<div id="content">
在我的 ContentComponent 中我可以使用
selector: '[id=content]'
将我的 Angular 应用加载到远程 Body