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