Angular 2中的dynamic模板url

过去几天我一直在玩Angular 2,想知道是否可以为@View装饰器提供一个dynamic的templateUrl

我曾尝试传递一个函数,并返回一个stringforms,但整个函数只是变成一个string。

我之前还没有真正使用过Angular 1.x,所以我不知道我是否只是以错误的方式来解决这个问题,但这是否可能,还是有更好的方法来创builddynamic视图?

例如,如果用户没有login,我可能想显示一个表单,但如果他们login,则显示一条文本消息。

像这样的东西不起作用:

 @Component({ selector: 'my-component' }) @View({ // This doesn't work templateUrl: function() { return this.isLoggedIn ? 'logged-in.html' : 'logged-out.html'; } }) class MyComponent { constructor() { this.loggedIn = false; } } 

任何帮助,将不胜感激。

虽然也许不是最优雅的解决scheme,但我使用DynamicComponentLoader和ElementRefdynamic地将模板值分配给组件。 实际上,我正在寻找一个解决scheme,可以将多个自定义组件添加到占位符中。

我尝试服务注入的function,由shmck概述,这是行不通的,因为当模板function被调用时,服务不可用。 的确, this是指Window对象。

我使用的解决scheme的参考URL可以在以下位置find: 在Angular2中使用ComponentResolver和ngFor创builddynamic锚点名称/组件

我也是用这种方式来指向Plnkr1和Plnkr2 。

网站Dartdocs提供了有关Angular 2 DynamicComponentLoader类的很好的文档,也适用于TypeScript。

简而言之:

一个简单的组件作为要使用的模板

 @Component({ selector: 'dt2-simple-block', properties: ["idx"], template: `<h1>Simple block for {{ idx }} </h1>`, directives: [] }) class dt2SimpleBlock { constructor() { } } 

持有所有要添加的组件的构造函数(我的应用程序需要包含多个子项:

  constructor(loader: DynamicComponentLoader, elementRef: ElementRef) { //iterate for (var i = 0; i < toSomething; i++) { // build the template var blockdirective = 'dt2-simple-block' var template = '<' + blockdirective + ' idx="' + this.userBlocks.userHomePanelBlocks[i] + '"></' + blockdirective + '>'; console.log(template); // debugging purpose var directives = [dt2SimpleBlock]; loader.loadNextToLocation(toComponent(template, directives), elementRef); } 

而辅助函数被放在某处作为util

 function toComponent(template, directives = []) { @Component({ selector: 'fake-component' }) @View({ template, directives }) class FakeComponent { } return FakeComponent; } 

我的解决scheme

Angular 2.0 ViewResolver类

 class myViewResolver extends ViewResolver{ resolve(component: Type): ViewMetadata { var view = super.resolve(component); // TODO: Write logic here:-) view.templateUrl = 'app/app.html'; return view; } } bootstrap(App,[ provide(ViewResolver , {useClass:myViewResolver}) ]); 

我的解决scheme:(关于这个漂亮的加载HTML和CSS文件)。

这是home.componenet.ts

 import { Component } from '@angular/core'; import { DynamicHTMLOutlet } from './../../directives/dynamic-html-outlet/dynamicHtmlOutlet.directive'; import { TranslateService, LangChangeEvent } from 'ng2-translate/ng2-translate'; @Component({ selector: 'lib-home', templateUrl: './app/content/home/home.component.html', directives: [DynamicHTMLOutlet] }) export class HomeComponent { html_template = `./app/content/home/home_`; html: string; css: string; constructor(translate: TranslateService) { this.html = this.html_template + translate.currentLang; this.css = './app/content/home/home.component.css'; translate.onLangChange.subscribe((event: LangChangeEvent) => { this.html = this.html_template + translate.currentLang; this.css = './app/content/home/home.component.css'; }); } } 

我使用的指令做了一些改变:这是在home.componenet.html

 <dynamic-html-outlet [htmlPath]="html" [cssPath]="css"></dynamic-html-outlet> 

这是dynamic组件的指令:

 import { Component, Directive, ComponentFactory, ComponentMetadata, ComponentResolver, Input, ReflectiveInjector, ViewContainerRef, } from '@angular/core'; import { TranslatePipe } from 'ng2-translate/ng2-translate'; declare var $:any; export function createComponentFactory(resolver: ComponentResolver, metadata: ComponentMetadata): Promise<ComponentFactory<any>> { const cmpClass = class DynamicComponent {}; const decoratedCmp = Component(metadata)(cmpClass); return resolver.resolveComponent(decoratedCmp); } @Directive({ selector: 'dynamic-html-outlet', }) export class DynamicHTMLOutlet { @Input() htmlPath: string; @Input() cssPath: string; constructor(private vcRef: ViewContainerRef, private resolver: ComponentResolver) { } ngOnChanges() { if (!this.htmlPath) return; $('dynamic-html') && $('dynamic-html').remove(); const metadata = new ComponentMetadata({ selector: 'dynamic-html', templateUrl: this.htmlPath +'.html', styleUrls: [this.cssPath], pipes: [TranslatePipe] }); createComponentFactory(this.resolver, metadata) .then(factory => { const injector = ReflectiveInjector.fromResolvedProviders([], this.vcRef.parentInjector); this.vcRef.createComponent(factory, 0, injector, []); }); } } 

不完全是你要求的,但值得一提的是:

另一个适用于大多数用例的简单解决scheme是将逻辑放在模板本身中,如下所示:

 @Component({ selector: 'my-component' }) @View({ // Note1: Here, I use template instead of templateUrl. // Note2: I use ES6 string interpolation + require() to embed/load the other templates, but you can do it however you like. template: ` <div [ngSwitch]="loggedIn"> <template [ngSwitchCase]="true"> ${require('./logged-in.html')} </template> <template ngSwitchDefault> ${require('./logged-out.html')} </template> </div>` }) class MyComponent { constructor() { this.loggedIn = false; } } 

这个解决scheme的缺点是,你服务的js文件最终包含了两个模板,所以这可能是一个大模板的问题(但只有一个模板被实际渲染,js的大小开销在许多情况下是可以接受的)。

更新到@Eyal Vardi的答案( ViewResolver已弃用):

 import { Directive, Type, Component } from '@angular/core'; import { DirectiveResolver } from '@angular/compiler'; class myViewUrlResolver extends DirectiveResolver { resolve(type: Type<any>, throwIfNotFound?: boolean): Directive { let view = <any>super.resolve(type, throwIfNotFound); if (typeof view["templateUrl"] !== "undefined") { console.log("Yay!"); let originalUrl = (<Component>view).templateUrl; (<Component> view).templateUrl = environment.nativeScriptAppPrePathPrefix + originalUrl.replace(".html", ".tns.html"); } if (typeof view["styleUrls"] !== "undefined") { console.log("Yay2!"); let originalUrls = (<Component>view).styleUrls; originalUrls.forEach((originalUrl, at) => (<Component>view).styleUrls[at] = environment.nativeScriptAppPrePathPrefix + originalUrl.replace(".html", ".tns.css")); } return view; } } platformNativeScriptDynamic().bootstrapModule(AppModule,{ providers: [ { provide: DirectiveResolver, useClass: myViewUrlResolver } ] }); 

看来这种创builddynamic模板的方式由于安全问题而不适用于Angular 2。 不幸的是,来自Angular 1,我以前的应用程序是这样dynamic驱动的。

对于Angular 2来说 – 这可能是一个不同的方式(下面的链接示例)。 通过将模板html文件更新为应用程序中的组件,然后将它们注入(尝试使用string等创buildtemplateUrl的位置),将组件模板参数视为元素(使用DynamicComponentLoader)。

https://angular.io/docs/js/latest/api/core/DynamicComponentLoader-class.html

希望, github的例子会帮助你! 有编译dynamichtml的例子。 所以,你可以通过你的任何服务加载HTML,然后编译它。

1-安装这个库

npm我-D html-loader

================================================== ==========

在webpack.config中,使用html-loader作为html文件

  { test: /\.html$/, loaders: ['html-loader'] } 

================================================== ==========

3-如果你使用离子,你可以从path“node_modules/@ionic/app-scripts/config/webpack.config.js”复制webpack.config.js,然后添加html加载器

================================================== ===========

4-如果你使用离子在package.json中添加这些行

 "config": { "ionic_bundler": "webpack", "ionic_webpack": "webpack.config.ionic.js" }, 

================================================== ===========

5 – 那么你可以使用它如下

 @Component({ selector: 'page-login', // templateUrl:"./login.html" template: function(){ if(globalVariables.test==2) { return require("./login2.html") } else { return require("./login.html") } }(), }) 

======================================

6 – 如果require函数有一个未解决的错误,你可以把它放在declarations.d.ts文件中,如下所示:

声明var require:any;

使用“ng serve –aot”编译您的应用程序。

 export let DEFAULT_PREFIX :string= './app.component'; //or localStorage.getItem('theme') export function getMySuperTemplate(template: string) { return DEFAULT_PREFIX + template + '.html'; } @Component({ selector: 'app-root', templateUrl: getMySuperTemplate('2'), styleUrls:['./app.component.css'] })