Динамические URL-адреса шаблонов в Angular 2


я играл с Angular 2 в течение последних нескольких дней и задавался вопросом, Можно ли обеспечить динамический templateUrl до @View оформителя.

Я попытался передать ему функцию и вернуть строковую форму, но вся функция просто get превратилась в строку.

Я действительно не использовал угловой 1.х до так что я не знаю, если я собираюсь об этом неправильно, но это возможно, или есть ли лучший способ для создания динамических мнения?

например, я могу захотеть отобразить форму, если пользователь не вошел в систему, но отобразить текстовое сообщение, если они вошли в систему.

что-то вроде это не работает:

@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 61

9 ответов:

хотя, возможно, не самое элегантное решение, я использовал DynamicComponentLoader и ElementRef для динамического присвоения значения шаблона компоненту. На самом деле, я искал решение, где я могу добавить несколько пользовательских компонентов в заполнителе.

Я попытался внедрить службу в функцию, как описано в shmck это не работает, поскольку службы еще не доступны при вызове функции шаблона. Действительно,this относится к окну объект.

ссылки URL для решения, которое я использовал, должны быть найдены на: создать динамическое имя привязки / компоненты с ComponentResolver и ngFor в Angular2

Я также ссылаюсь на этот способ 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;
}

мое решение:

Угловой 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})
]);

Не совсем то, что вы просили, но стоит отметить:

еще одно простое решение, которое работает для большинства случаев использования, заключается в том, чтобы поместить логику в сам шаблон, например:

@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).

мое решение: (красота в этом-ленивая загрузка для файлов html и css.)

Это дом.componenet.ТС

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

 }

директива, которую я использовал и сделал несколько изменений: Это у нас дома.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, []);
      });
  }
}

обновление до ответа @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 } 
  ]
});

1 - установите эту библиотеку

npm i-D html-loader

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

2-In webpack.config использовать html-загрузчик для html файлов

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

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

3-Если вы используете ионный, вы можете скопировать webpack.конфиг.js с пути "node_modules / @ionic/app-scripts/config / webpack.конфиг.js" затем добавьте загрузчик html в это

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

4-Если вы используете ионный В комплексе.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, вы можете поместить ее в объявления.D. TS файл, как показано ниже :

объявить var require: any;

скомпилируйте приложение с помощью 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']
})

похоже, этот способ создания динамических шаблонов не будет доступен для Angular 2 из-за вопросов безопасности. К сожалению, исходя из углового 1 мое предыдущее приложение было динамически управляется таким образом.

для углового 2-это может быть differeny способ сделать то же самое (пример ссылки ниже). Обновляя HTML-файлы шаблона, чтобы они были компонентами в приложении, а затем вставляя их в (место, где вы пытались создать templateUrl со строкой и т. д.) представление параметр шаблона компонента в виде элементов (с помощью DynamicComponentLoader).

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

Надежда, пример github для вас поможет вам! Есть пример для компиляции динамического html. Таким образом, вы можете загрузить HTML с помощью любого из ваших сервисов, а затем скомпилировать его.