Динамические 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 ответов:
хотя, возможно, не самое элегантное решение, я использовал 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 с помощью любого из ваших сервисов, а затем скомпилировать его.