Angular 远程加载模块不会将入口组件加载到视图中
Posted
技术标签:
【中文标题】Angular 远程加载模块不会将入口组件加载到视图中【英文标题】:Angular Remotely loaded Module does not load entry component into view 【发布时间】:2021-06-16 06:30:34 【问题描述】:背景:
我已经按照多个教程远程加载模块,以便尝试使用 Angular 创建插件架构。特别是:
我将 Angular 10 用于主应用程序 用于构建插件的角度构建器 汇总以生成 UMD 模块。 SystemJS 作为模块加载器手头的问题:
我可以成功加载远程定义的模块,并且远程模块可以成功使用公共服务(我的意思是主或核心应用程序和插件都知道) 我无法动态加载该模块中定义的组件,即使该组件是在插件模块声明、导出和模块本身中的入口组件中定义的。代码如下:
https://github.com/rickszyr/angular-plugins/
如何运行:
-
npm 安装
npm run build:init //编译通用服务
npm run build:plugins // 为两个插件生成 umd 包
npm run start:all // 启动服务器和客户端
使用默认字段值单击“加载”
出现错误。
错误:
我发现由于某种原因,组件主机视图没有初始化 _lview 值。但我不确定如何处理这些信息或如何确保它确实具有正确设置的值。
尝试创建组件并将其插入动态组件加载器时,失败的行位于 app.component.ts 中。
非常感谢您
主要组件:
app.component.ts
import Compiler, Component, ComponentFactoryResolver, Injector, NgModuleFactory, ViewChild, ViewContainerRef from "@angular/core";
import HttpClient from "@angular/common/http";
import IPlugin, PluginCatalogService from "interfaces";
import * as ngCore from "@angular/core";
import * as ngCommon from "@angular/common";
import * as ngBrowser from "@angular/platform-browser";
import * as commonInterfaces from "interfaces";
import ModuleLoader from "./remote-module-loader.service";
import DynamicComponentDirective from "./directives/dynamic-component.directive";
@Component(
selector: "app-root",
templateUrl: "app.component.html",
styles: [],
)
export class AppComponent
title = "plugins";
loader: ModuleLoader;
@ViewChild('putStuffHere', read: ViewContainerRef) putStuffHere: ViewContainerRef;
constructor(
public pluginService: PluginCatalogService,
private injector: Injector,
private factoryResolver: ComponentFactoryResolver,
private compiler: Compiler,
public viewContainer: ViewContainerRef
)
this.loader = new ModuleLoader();
loadModule(modulePath: string, moduleName: string)
this.loader.register(
"@angular/core": ngCore,
"@angular/common": ngCommon,
"interfaces": commonInterfaces
).then(ml => ml.load(modulePath).then(m =>
const moduleFactory: NgModuleFactory<any> = <NgModuleFactory<any>>m.default[moduleName+ "NgFactory"];
const moduleReference = moduleFactory.create(this.injector);
moduleReference.componentFactoryResolver.resolveComponentFactory((<IPlugin>moduleReference.instance).mainComponent);
var compFactory = moduleReference.componentFactoryResolver.resolveComponentFactory(this.getEntryComponent(moduleFactory));
this.putStuffHere.createComponent(compFactory); // <<< this fails
var component = compFactory.create(this.injector);
this.putStuffHere.insert(component.hostView);// <<< this fails
));
getEntryComponent(moduleFactory: any):any
var existModuleLoad = (<any>moduleFactory.moduleType).decorators[0].type.prototype.ngMetadataName === "NgModule"
if (!existModuleLoad) return null;
return moduleFactory.moduleType.decorators[0].args[0].entryComponents[0];
app.component.html
<h1>Welcome!</h1>
<p>
<label>Path Remote</label><input #pathRemote value="http://localhost:3000/plugin2.module.umd.js">
</p>
<p>
<label>Remote Name</label><input #remoteName value="Plugin2Module">
</p>
<p>
<button (click)="loadModule(pathRemote.value, remoteName.value)">Load</button>
</p>
<ol>
<li *ngFor="let module of pluginService.installedPlugins"> module.name</li>
</ol>
<ng-container #putStuffHere></ng-container>
编译的插件代码:
(function (global, factory)
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core'), require('@angular/common'), require('interfaces')) :
typeof define === 'function' && define.amd ? define(['exports', '@angular/core', '@angular/common', 'interfaces'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Plugin2Module = , global.i0, global.i3, global.i4));
(this, (function (exports, i0, i3, i4) 'use strict';
/**
* @fileoverview added by tsickle
* Generated from: lib/plugin2.component.ts
* @suppress checkTypes,constantProperty,extraRequire,missingOverride,missingRequire,missingReturn,unusedPrivateMembers,uselessCode checked by tsc
*/
class Plugin2Component
constructor()
this.title = "Nada";
/**
* @return ?
*/
ngOnInit()
Plugin2Component.decorators = [
type: i0.Component, args: [
selector: 'lib-plugin2',
template: `
<p>
plugin2 works!
</p>
`
]
];
/** @nocollapse */
Plugin2Component.ctorParameters = () => [];
Plugin2Component.propDecorators =
title: [ type: i0.Input ]
;
/**
* @fileoverview added by tsickle
* Generated from: lib/plugin2.module.ts
* @suppress checkTypes,constantProperty,extraRequire,missingOverride,missingRequire,missingReturn,unusedPrivateMembers,uselessCode checked by tsc
*/
class Plugin2Module
/**
* @param ? pluginService
*/
constructor(pluginService)
console.log("Se registro Plugin 2");
pluginService.installedPlugins.push(this);
/**
* @return ?
*/
get name()
return "Plugin 2";
/**
* @return ?
*/
get mainComponent()
return Plugin2Component;
Plugin2Module.decorators = [
type: i0.NgModule, args: [
declarations: [Plugin2Component],
imports: [i3.CommonModule],
exports: [Plugin2Component],
entryComponents: [Plugin2Component]
,]
];
/** @nocollapse */
Plugin2Module.ctorParameters = () => [
type: i4.PluginCatalogService
];
/**
* @fileoverview This file was generated by the Angular template compiler. Do not edit.
*
* @suppress suspiciousCode,uselessCode,missingProperties,missingOverride,checkTypes,extraRequire
* tslint:disable
*/
var styles_Plugin2Component = [];
var RenderType_Plugin2Component = i0.ɵcrt( encapsulation: 2, styles: styles_Plugin2Component, data: );
function View_Plugin2Component_0(_l) return i0.ɵvid(0, [(_l()(), i0.ɵeld(0, 0, null, null, 1, "p", [], null, null, null, null, null)), (_l()(), i0.ɵted(-1, null, [" plugin2 works! "]))], null, null);
function View_Plugin2Component_Host_0(_l) return i0.ɵvid(0, [(_l()(), i0.ɵeld(0, 0, null, null, 1, "lib-plugin2", [], null, null, null, View_Plugin2Component_0, RenderType_Plugin2Component)), i0.ɵdid(1, 114688, null, 0, Plugin2Component, [], null, null)], function (_ck, _v) _ck(_v, 1, 0); , null);
var Plugin2ComponentNgFactory = i0.ɵccf("lib-plugin2", Plugin2Component, View_Plugin2Component_Host_0, title: "title" , , []);
/**
* @fileoverview This file was generated by the Angular template compiler. Do not edit.
*
* @suppress suspiciousCode,uselessCode,missingProperties,missingOverride,checkTypes,extraRequire
* tslint:disable
*/
var Plugin2ModuleNgFactory = i0.ɵcmf(Plugin2Module, [], function (_l) return i0.ɵmod([i0.ɵmpd(512, i0.ComponentFactoryResolver, i0.ɵCodegenComponentFactoryResolver, [[8, [Plugin2ComponentNgFactory]], [3, i0.ComponentFactoryResolver], i0.NgModuleRef]), i0.ɵmpd(4608, i3.NgLocalization, i3.NgLocaleLocalization, [i0.LOCALE_ID]), i0.ɵmpd(1073742336, i3.CommonModule, i3.CommonModule, []), i0.ɵmpd(1073742336, Plugin2Module, Plugin2Module, [i4.PluginCatalogService])]); );
exports.Plugin2ModuleNgFactory = Plugin2ModuleNgFactory;
Object.defineProperty(exports, '__esModule', value: true );
)));
插件的tsconfig.lib.json:
/* To learn more about this file see: https://angular.io/config/tsconfig. */
"extends": "../../tsconfig.json",
"compilerOptions":
"outDir": "../../out-tsc/lib",
"target": "es2015",
"declaration": true,
"declarationMap": true,
"inlineSources": true,
"types": [],
"lib": [
"dom",
"es2018"
]
,
"angularCompilerOptions":
"enableIvy": false,
"skipTemplateCodegen": false,
"strictMetadataEmit": true,
"annotateForClosureCompiler": true,
"enableResourceInlining": true
,
"exclude": [
"src/test.ts",
"**/*.spec.ts"
]
【问题讨论】:
【参考方案1】:您在插件中禁用了 Ivy 编译器,但忘记在主项目中禁用它。
在主tsconfig.json
中添加以下内容将解决问题
"angularCompilerOptions":
"enableIvy": false
感谢@DWhitSlaya 在此提及这一点:https://www.reddit.com/r/angular/comments/ih7hib/how_to_use_viewcontainerref_with_dynamic/g30hpzv
【讨论】:
是的!经过大量的谷歌搜索,我能够弄清楚这一点。太感谢了。我现在还设法在没有禁用常春藤的情况下运行所有内容,这太棒了,因为这是前进的方向以上是关于Angular 远程加载模块不会将入口组件加载到视图中的主要内容,如果未能解决你的问题,请参考以下文章