Url de plantilla dinámica en Angular 2


He estado jugando con Angular 2 durante los últimos días y me pregunté si era posible proporcionar una dinámica templateUrl al decorador @View.

He intentado pasarle una función y devolverle una cadena, pero toda la función acaba de convertirse en una cadena.

Realmente no he usado Angular 1.x antes de cualquiera de los dos, así que no se si estoy haciendo esto de la manera equivocada, pero es esto posible, o hay una mejor manera de crear vistas dinámicas?

Para ejemplo Es posible que desee mostrar un formulario si el usuario no ha iniciado sesión, pero mostrar un mensaje de texto si ha iniciado sesión.

Algo como esto no funciona:

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

Cualquier ayuda sería apreciada.

Author: Jake Lucas, 2015-07-29

9 answers

Aunque quizás no sea la solución más elegante, utilicé DynamicComponentLoader y ElementRef para asignar dinámicamente un valor de plantilla a un componente. De hecho, estaba buscando una solución donde pueda agregar múltiples componentes personalizados en un marcador de posición.

Probé la inyección de servicios en la función como lo describe shmck esto no funciona ya que los servicios no están disponibles todavía cuando se llama a la función de plantilla. De hecho, this se refiere al objeto Window.

Url de referencia para la solución que utilicé se encuentra en: crear anchorName/Componentes dinámicos con ComponentResolver y ngFor en Angular2

También me refiero a Plnkr1y Plnkr2.

El sitio Dartdocs proporciona una buena documentación sobre la clase Angular 2 DynamicComponentLoader, también aplicable a TypeScript.

En resumen:

Un componente simple como la plantilla a utilizar

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

Constructor del componente que contiene todos Componentes que se agregarán (mi aplicación requiere que se incluyan varios niños:

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

Y la función auxiliar que se pondrá en algún lugar como útil

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

  return FakeComponent;
}
 23
Author: TomG,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:02:48

Mi solución:

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})
]);
 10
Author: Eyal Vardi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-04-07 05:13:41

No es exactamente lo que pediste, pero vale la pena mencionarlo:

Otra solución simple, que funciona para la mayoría de los casos de uso, es poner la lógica en la propia plantilla, así:

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

La desventaja de esta solución es que su archivo js servido termina conteniendo ambas plantillas, por lo que esto podría ser un problema para las plantillas grandes (pero solo una plantilla se renderiza realmente y la sobrecarga de tamaño js es aceptable en muchos casos).

 6
Author: Yoav Aharoni,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-20 13:33:20

Mi solución:(La belleza de esto es la carga lenta para archivos html y css.)

Este es el hogar.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';
    });
  }

 }

La directiva que utilicé e hice algunos cambios: Esto es en casa.componenet.html

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

Esta es la directiva para componentes dinámicos:

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, []);
      });
  }
}
 5
Author: Daniel abzakh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-19 17:28:32

Actualización de la respuesta de @Eyal Vardi (ViewResolver está obsoleta):

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 } 
  ]
});
 3
Author: Sebas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-23 11:20:57

1-instalar esta biblioteca

Npm i-D html-loader

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

2 - En webpack.config use html-loader para archivos html

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

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

3 - Si usa ionic , puede copiar webpack.config.js del camino "node_modules / @ionic/app-scripts/config / webpack.config.js" luego agregue el cargador html a it

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

4-Si usa ionic En paquete.json añadir estas líneas

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

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

5 - Entonces usted puede utilizarlo como abajo

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

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

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

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

6-Si hay un error sin resolver con la función require, puede ponerlo en declaraciones.d. ts archivo como el siguiente:

Declare var require: any;

 2
Author: Bahgat Mashaly,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-10 09:07:14

Compile su aplicación con aot "ng serve a 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']
})
 2
Author: Weslley De Souza,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-21 02:02:34

Parece que esta forma de crear plantillas dinámicas no estará disponible para Angular 2 debido a cuestiones de seguridad. Desafortunado, viniendo de Angular 1 mi aplicación anterior fue impulsada dinámicamente de esta manera.

Para Angular 2 - Esto podría ser una forma diferente de hacer lo mismo (ejemplo de enlace a continuación). Actualizando los archivos html de la plantilla para que sean componentes en la aplicación, luego inyectándolos en (el lugar donde estaba tratando de crear la templateUrl con una cadena, etc.) componente de vista parámetro de plantilla como elementos (usando DynamicComponentLoader).

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

 1
Author: Nick Taras,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-12-21 23:10:40

Esperanza, github ejemplo para usted le ayudará! Hay un ejemplo para compilar html dinámico. Por lo tanto, puede cargar HTML por cualquiera de su Servicio y luego compilarlo.

 1
Author: Ivan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-14 22:27:53