Dynamic template URLs in Angular 2

2019-01-02 17:50发布

I've been playing around with Angular 2 for the past few days and wondered if it was possible to provide a dynamic templateUrl to the @View decorator.

I have tried passing it a function and returning a string form it but the entire function just get's turned into a string.

I haven't really used Angular 1.x before either so I don't know if I'm just going about this in the wrong way, but is this possible, or is there a better way to create dynamic views?

For example I might want to display a form if the user is not logged in, but display a text message if they are logged in.

Something like this doesn't work:

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

Any help would be appreciated.

9条回答
余生无你
2楼-- · 2019-01-02 17:58

1- install this library

npm i -D html-loader

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

2- In webpack.config use html-loader for html files

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

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

3- If you use ionic , you can copy webpack.config.js from the path "node_modules/@ionic/app-scripts/config/webpack.config.js" then add the html loader to it

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

4-If you use ionic In package.json add these lines

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

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

5-Then you can use it as below

@Component({
  selector: 'page-login',
 // templateUrl:"./login.html"

   template:     function(){
    if(globalVariables.test==2) {

      return require("./login2.html")
    }
    else
    {
      return require("./login.html")
    }
  }(),
})

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

6-If there is unresolved error with require function you can put it in declarations.d.ts file as the below :

declare var require: any;

查看更多
唯独是你
3楼-- · 2019-01-02 18:04

It appears this way of creating dynamic templates will not be available to Angular 2 due to security matters. Unfortunate, coming from Angular 1 my previous application was dynamically driven this way.

For Angular 2 - This could be a differeny way of doing the same (link example below). By updating the template html files to be components in the application, then injecting them into (the place where you were trying to create the templateUrl with a string etc) view component template parameter as elements (using DynamicComponentLoader).

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

查看更多
柔情千种
4楼-- · 2019-01-02 18:08

Compile your application with aot "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']
})
查看更多
无色无味的生活
5楼-- · 2019-01-02 18:10

My solution:

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})
]);
查看更多
伤终究还是伤i
6楼-- · 2019-01-02 18:14

Not quite what you asked for, but it's worth mentioning:

Another simple solution, which works for most use cases, is to put the logic in the template itself, like so:

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

Downside for this solution is that your served js file ends up containing both templates, so this might be an issue for big templates (but only one template is actually rendered and the js size overhead is acceptable in many cases).

查看更多
何处买醉
7楼-- · 2019-01-02 18:14

Update to @Eyal Vardi's answer (ViewResolver is deprecated):

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 } 
  ]
});
查看更多
登录 后发表回答