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

10

我一直在到处寻找解决方案。

我的项目有不同的“皮肤”,基本上是不同的模板/Css集合。

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

不幸的是,我不能找到如何实现这一点。我查看了angular.io上的动态组件加载器,但没有成功。

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

有人有想法吗?

这是我迄今为止尝试过的:

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个回答

15

你可以这样做:

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);
      })
  }
}

请确保URL正确,并将模板加载到客户端。

阅读这里是关于Angular动态组件需要了解的内容以获取更多详细信息。


1
刚刚在全新的Angular cli项目上尝试了一下,使用webpack运行良好。你使用什么构建过程? - Max Koretskyi
1
我使用一个ionic样板文件。当我在模板路径中放置一个变量(请参见我的代码中的THEME_DIR)时,它会抱怨,因为似乎找不到正确的路径。 - millerf
2
我明白了,不幸的是我对此不熟悉。我认为您应该创建另一个问题,因为它与我回答的问题并不相关。 - Max Koretskyi
@angularindepth-com 我已经成功让它工作了,但是我在使用AoT时仍然遇到了一些问题。无论如何,现在它已经被搁置了...你是否也遇到了同样的问题? - millerf
@millerf,不,我回答了你的问题:)。你没有接受它,所以我想知道它是否解决了你的问题。 - Max Koretskyi
显示剩余29条评论

2
我在尝试从服务器动态加载模板时遇到了问题(我想在提供HTML之前进行安全检查和服务器端翻译)。
我在更改webpack配置后解决了这个问题。实际上,在执行ng eject后,它创建了一个包含.ts加载器@ngtools/webpack的webpack.config.js文件:
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(Ahead Of Time)。根据文档: 在选项部分中,ngtools提到: skipCodeGeneration。可选,默认为false。禁用代码生成并不重构引导代码。这将替换templateUrl:“string”为template:require(“string”) 如果您不希望编译AOT的templateUrl,请删除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

ng eject不再受支持。我不建议使用这个解决方案。这个想法是让Angular编译模板。 - Yacine MEDDAH

1

从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']
    })

2
是的,在编译时很酷,但运行时呢? - DARKGuy
@DARKGuy 你有没有在运行时让它工作? - Prabhakaran
@Prabhakaran 不是,但我也不是 OP。不确定 2022 年是否有新的方法来做这件事。应该会有的... - DARKGuy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接