теперь можно использовать исполняющий компилятор в Angular для загрузки модулей, которые были упакованы с веб-пакетом, используя webpack-system-register
я есть угловое приложение, созданное с использованием Typescript и связанное с веб-пакетом. Здесь нет ничего необычного. То, что я хочу сделать, это разрешить плагины во время выполнения, что означает, что компоненты и / или модули за пределами пакета также должны иметь возможность регистрироваться в приложении. До сих пор я пытался включить другой пакет веб-пакета в index.html и использовать массив Implict, чтобы вставить в него упомянутый модуль / компонент, и импортировать их в мой модуль.
Посмотрите, что импорт использует переменную Implict. Это работает для модулей внутри пакета, но модули из другого пакета не будут работать.
@NgModule({
imports: window["app"].modulesImport,
declarations: [
DYNAMIC_DIRECTIVES,
PropertyFilterPipe,
PropertyDataTypeFilterPipe,
LanguageFilterPipe,
PropertyNameBlackListPipe
],
exports: [
DYNAMIC_DIRECTIVES,
CommonModule,
FormsModule,
HttpModule
]
})
export class PartsModule {
static forRoot()
{
return {
ngModule: PartsModule,
providers: [ ], // not used here, but if singleton needed
};
}
}
Я также попытался создать модуль и компонент с использованием кода es5, как показано ниже, и вставить то же самое в мой массив модулей:
var HelloWorldComponent = function () {
};
HelloWorldComponent.annotations = [
new ng.core.Component({
selector: 'hello-world',
template: '<h1>Hello World!</h1>',
})
];
window["app"].componentsLazyImport.push(HelloWorldComponent);
Оба подхода приводят к следующей ошибке:
ncaught Error: Unexpected value 'ExtensionsModule' imported by the module 'PartsModule'. Please add a @NgModule annotation.
at syntaxError (http://localhost:3002/dist/app.bundle.js:43864:34) [<root>]
at http://localhost:3002/dist/app.bundle.js:56319:44 [<root>]
at Array.forEach (native) [<root>]
at CompileMetadataResolver.getNgModuleMetadata (http://localhost:3002/dist/app.bundle.js:56302:49) [<root>]
at CompileMetadataResolver.getNgModuleSummary (http://localhost:3002/dist/app.bundle.js:56244:52) [<root>]
at http://localhost:3002/dist/app.bundle.js:56317:72 [<root>]
at Array.forEach (native) [<root>]
at CompileMetadataResolver.getNgModuleMetadata (http://localhost:3002/dist/app.bundle.js:56302:49) [<root>]
at CompileMetadataResolver.getNgModuleSummary (http://localhost:3002/dist/app.bundle.js:56244:52) [<root>]
at http://localhost:3002/dist/app.bundle.js:56317:72 [<root>]
at Array.forEach (native) [<root>]
at CompileMetadataResolver.getNgModuleMetadata (http://localhost:3002/dist/app.bundle.js:56302:49) [<root>]
at JitCompiler._loadModules (http://localhost:3002/dist/app.bundle.js:67404:64) [<root>]
at JitCompiler._compileModuleAndComponents (http://localhost:3002/dist/app.bundle.js:67363:52) [<root>]
Обратите внимание, что если я пытаюсь использовать компонент вместо модуля, я вместо этого помещаю их в объявления, что приводит к соответствующей ошибке для компонентов, говорящих о необходимости добавить аннотацию @ pipe / @ component.
Я чувствую, что это должно быть выполнимо, но я не знаю, чего мне не хватает. Я использую [email protected]
обновление 05.11.2017
Поэтому я решил сделать шаг назад и начать все с нуля. Вместо того, чтобы использоватьWebPack Я решил попробовать сSystemJS вместо этого, как я нашел основной компонент в Angular. На этот раз я работал, используя следующие компоненты и службы для вставки компонентов:
typebuilder.ts
import { Component, ComponentFactory, NgModule, Input, Injectable, CompilerFactory } from '@angular/core';
import { JitCompiler } from '@angular/compiler';
import {platformBrowserDynamic} from "@angular/platform-browser-dynamic";
export interface IHaveDynamicData {
model: any;
}
@Injectable()
export class DynamicTypeBuilder {
protected _compiler : any;
// wee need Dynamic component builder
constructor() {
const compilerFactory : CompilerFactory = platformBrowserDynamic().injector.get(CompilerFactory);
this._compiler = compilerFactory.createCompiler([]);
}
// this object is singleton - so we can use this as a cache
private _cacheOfFactories: {[templateKey: string]: ComponentFactory<IHaveDynamicData>} = {};
public createComponentFactoryFromType(type: any) : Promise<ComponentFactory<any>> {
let module = this.createComponentModule(type);
return new Promise((resolve) => {
this._compiler
.compileModuleAndAllComponentsAsync(module)
.then((moduleWithFactories : any) =>
{
let _ = window["_"];
let factory = _.find(moduleWithFactories.componentFactories, { componentType: type });
resolve(factory);
});
});
}
protected createComponentModule (componentType: any) {
@NgModule({
imports: [
],
declarations: [
componentType
],
})
class RuntimeComponentModule
{
}
// a module for just this Type
return RuntimeComponentModule;
}
}
Dynamic.component.ts
import { Component, Input, ViewChild, ViewContainerRef, SimpleChanges, AfterViewInit, OnChanges, OnDestroy, ComponentFactory, ComponentRef } from "@angular/core";
import { DynamicTypeBuilder } from "../services/type.builder";
@Component({
"template": '<h1>hello dynamic component <div #dynamicContentPlaceHolder></div></h1>',
"selector": 'dynamic-component'
})
export class DynamicComponent implements AfterViewInit, OnChanges, OnDestroy {
@Input() pathToComponentImport : string;
@ViewChild('dynamicContentPlaceHolder', {read: ViewContainerRef})
protected dynamicComponentTarget: ViewContainerRef;
protected componentRef: ComponentRef<any>;
constructor(private typeBuilder: DynamicTypeBuilder)
{
}
protected refreshContent() : void {
if (this.pathToComponentImport != null && this.pathToComponentImport.indexOf('#') != -1) {
let [moduleName, exportName] = this.pathToComponentImport.split("#");
window["System"].import(moduleName)
.then((module: any) => module[exportName])
.then((type: any) => {
this.typeBuilder.createComponentFactoryFromType(type)
.then((factory: ComponentFactory<any>) =>
{
// Target will instantiate and inject component (we'll keep reference to it)
this.componentRef = this
.dynamicComponentTarget
.createComponent(factory);
// let's inject @Inputs to component instance
let component = this.componentRef.instance;
component.model = { text: 'hello world' };
//...
});
});
}
}
ngOnDestroy(): void {
}
ngOnChanges(changes: SimpleChanges): void {
}
ngAfterViewInit(): void {
this.refreshContent();
}
}
Теперь я могу ссылаться на любой данный компонент, как это:
<dynamic-component pathToComponentImport="/app/views/components/component1/extremely.dynamic.component.js#ExtremelyDynamicComponent"></dynamic-component>
Конфигурация Typescript:
{
"compilerOptions": {
"target": "es5",
"module": "system",
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
"allowJs": true,
"experimentalDecorators": true,
"lib": [ "es2015", "dom" ],
"noImplicitAny": true,
"suppressImplicitAnyIndexErrors": true
},
"exclude": [
"node_modules",
"systemjs-angular-loader.js",
"systemjs.config.extras.js",
"systemjs.config.js"
]
}
И выше моего конфига машинописи. Так что это работает, однако я не уверен, что я доволен использованием SystemJS. Я чувствую, что это должно быть возможно и с веб-пакетом, и я не уверен, что именно так TC компилирует файлы, которых не понимает веб-пакет ... Я все еще получаю исключение отсутствующего декоратора, если я пытаюсь запустить этот код в комплекте веб-пакета ,
С наилучшими пожеланиями Мортен