具有动态模板或templateUrl的Angular 2/4组件

时间:2017-07-28 14:56:49

标签: angular typescript themes

我一直在努力为此找到解决方案。

我有一个包含不同'皮肤的项目,它们基本上是不同的模板/ Css集。

我正在尝试让我的组件使用基于变量THEME_DIR的皮肤。

不幸的是,我无法找到如何实现这一目标。我调查了angular.io上的Dynamic Component Loader但没有成功。

我也在这里看了几个答案但没有成功。

有没有人有想法?

这是我到目前为止所尝试的:

import { ComponentFactoryResolver, ViewContainerRef } from '@angular/core';

// @Component({
//     templateUrl: '../../assets/theme/'+THEME_DIR+'/login.template.html',
// })

export class LoginComponent implements, AfterViewInit {


    private log = Log.create('LoginPage');

    constructor(private mzksLsRequestService: MzkLsRequestService,
                private componentFactoryResolver: ComponentFactoryResolver,
                public viewContainerRef: ViewContainerRef) {
    }



    ngAfterViewInit() {
        let componentFactory = this.componentFactoryResolver.resolveComponentFactory(new Component({
            templateUrl: '../../assets/theme/default/login.template.html',
        }));
        let viewContainerRef = this.viewContainerRef;
        viewContainerRef.clear();
        let componentRef = viewContainerRef.createComponent(componentFactory);

    }

}

3 个答案:

答案 0 :(得分:12)

你可以这样做:

import {
  Compiler, Component, Injector, VERSION, ViewChild, NgModule, NgModuleRef,
  ViewContainerRef
} from '@angular/core';


@Component({
  selector: 'my-app',
  template: `
      <h1>Hello {{name}}</h1>
      <ng-container #vc></ng-container>
  `
})
export class AppComponent {
  @ViewChild('vc', {read: ViewContainerRef}) vc;
  name = `Angular! v${VERSION.full}`;

  constructor(private _compiler: Compiler,
              private _injector: Injector,
              private _m: NgModuleRef<any>) {
  }

  ngAfterViewInit() {
    const tmpCmp = Component({
        moduleId: module.id, templateUrl: './e.component.html'})(class {
    });
    const tmpModule = NgModule({declarations: [tmpCmp]})(class {
    });

    this._compiler.compileModuleAndAllComponentsAsync(tmpModule)
      .then((factories) => {
        const f = factories.componentFactories[0];
        const cmpRef = f.create(this._injector, [], null, this._m);
        cmpRef.instance.name = 'dynamic';
        this.vc.insert(cmpRef.hostView);
      })
  }
}

只需确保网址正确并将模板加载到客户端。

阅读Here is what you need to know about dynamic components in Angular了解详情。

答案 1 :(得分:1)

我在尝试从服务器加载动态模板时遇到了问题(我想在服务html之前进行安全检查,在服务器端进行翻译。

我在更改webpack配置后解决了这个问题。实际上,在执行ng eject之后,它创建了一个webpack.config.js,其中包含.ts loader @ngtools/webpack和:

new AotPlugin({
  "mainPath": "main.ts",
  "replaceExport": false,
  "hostReplacementPaths": {
    "environments\\environment.ts": "environments\\environment.ts"
  },
  "exclude": [],
  "tsConfigPath": "src/main/front/tsconfig.app.json",
  "skipCodeGeneration": true
})

这最后一个,是问题的根源。它涉及AOT(提前)。根据文件: 选项部分的ngtools,它被提及:

  

skipCodeGeneration。可选,默认为false。禁用代码   生成并且不要将代码重构为bootstrap。 这将模板替换为templateUrl:“string”:require(“string”)

如果您不希望您的templateUrl被编译为AOT,我建议您删除AotPlugin,并使用ts-loader而不是@ ngtools / webpack,请参阅:

ts-loader

ts的规则如下:

{
    test: /\.tsx?$/,
    loader: 'ts-loader'
}

现在,您可以根据需要从相对URL加载新模板。示例:

@Component({
    selector : "custom-component",
    templateUrl : "/my_custom_url_on_server"
})
export class CustomComponent {
}

请参阅Issue

答案 2 :(得分:0)

从 Ivy 开始(我认为)我们可以在 templateUrl 中使用静态变量(例如环境)。 例如:

    import { environment } from 'src/environments/environment';
    @Component({
        selector: 'home',
        templateUrl: `./skins/${environment.skin}/home.page.html`,
        styleUrls: ['./skins/${environment.skin}/home.page.scss']
    })