首页 文章

Angular 2中的动态模板URL

提问于
浏览
63

过去几天我一直在玩Angular 2,并想知道是否有可能为 @View 装饰提供动态 templateUrl .

我试过传递一个函数并返回一个字符串形式,但整个函数只是变成了一个字符串 .

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

例如,如果用户未登录,我可能希望显示表单,但如果他们已登录则显示文本消息 .

这样的东西不起作用:

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

任何帮助,将不胜感激 .

9 回答

  • 23

    虽然可能不是最优雅的解决方案,但我使用DynamicComponentLoader和ElementRef动态地将模板值分配给组件 . 事实上,我正在寻找一种解决方案,我可以在占位符中添加多个自定义组件 .

    我在函数中尝试了服务注入,如shmck所述,这不起作用,因为调用模板函数时服务还不可用 . 实际上, this 指的是Window对象 .

    我使用的解决方案的参考URL可在以下位置找到:create dynamic anchorName/Components with ComponentResolver and ngFor in Angular2

    我也将这种方式引用到Plnkr1Plnkr2 .

    网站Dartdocs提供了有关Angular 2 DynamicComponentLoader类的精彩文档,也适用于TypeScript .

    简而言之:

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

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

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

    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;
    }
    
  • 3

    我的解决方案

    Angular 2.0 ViewResolver Class

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

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

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

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

    此解决方案的缺点是您提供的服务js文件最终包含两个模板,因此这可能是大模板的问题(但实际上只渲染了一个模板,并且在许多情况下js大小开销是可接受的) .

  • 1

    我的解决方案:(关于这个的美丽是对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>
    

    这是动态组件的指令:

    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, []);
          });
      }
    }
    
  • 1

    更新@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 } 
      ]
    });
    
  • 5

    使用“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']
    })
    
  • 10

    由于安全问题,Angular 2无法使用这种创建动态模板的方式 . 不幸的是,来自Angular 1的我以前的应用程序是以这种方式动态驱动的 .

    对于Angular 2 - 这可能是一种不同的方式(下面的链接示例) . 通过将模板html文件更新为应用程序中的组件,然后将它们注入(您尝试使用字符串等创建templateUrl的位置)视图组件模板参数作为元素(使用DynamicComponentLoader) .

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

  • 1

    希望,github example for you会帮助你!有编译动态html的例子 . 因此,您可以通过任何服务加载HTML,然后进行编译 .

  • 2

    1-安装此库

    npm i -D html-loader

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

    2-在webpack.config中使用html-loader获取html文件

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

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

    3-如果使用ionic,可以从路径“node_modules/@ionic/app-scripts/config/webpack.config.js”复制webpack.config.js,然后将html加载器添加到其中

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

    4 - 如果你使用ionic In 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;

相关问题