URLs de modelo dynamic no Angular 2

Eu tenho brincado com o Angular 2 nos últimos dias e me perguntei se era possível fornecer um templateUrl dynamic para o decorador @View .

Eu tentei passar uma function e retornar uma forma de string, mas a function inteira acabou de se transformar em uma string.

Eu realmente não usei Angular 1.x antes, então eu não sei se estou indo sobre isso da maneira errada, mas isso é possível, ou existe uma maneira melhor de criar views dinâmicas?

Por exemplo, talvez eu queira exibir um formulário se o usuário não estiver logado, mas exibir uma mensagem de texto, se eles estiverem conectados.

Algo assim não 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; } } 

Qualquer ajuda seria apreciada.

Embora talvez não seja a solução mais elegante, usei DynamicComponentLoader e ElementRef para atribuir dinamicamente o valor do modelo a um componente. Na verdade, eu estava procurando uma solução onde eu possa adicionar vários componentes personalizados em um espaço reservado.

Eu tentei injeção de serviço na function como descrito por shmck isso não funciona como os serviços não estão disponíveis ainda quando a function de modelo é chamada. De fato, this se refere ao object Window.

URLs de referência para a solução que usei devem ser encontradas em: create dynamic anchorName / Components com ComponentResolver e ngFor em Angular2

Eu também me refiro a este caminho para Plnkr1 e Plnkr2 .

O site Dartdocs fornece uma boa documentação sobre a class Angular 2 DynamicComponentLoader, também aplicável ao TypeScript.

Em resumo:

Um componente simples como o modelo a ser usado

 @Component({ selector: 'dt2-simple-block', properties: ["idx"], template: `

Simple block for {{ idx }}

`, directives: [] }) class dt2SimpleBlock { constructor() { } }

Construtor do Componente que contém todos os Componentes a serem adicionados (meu aplicativo requer que vários childs sejam incluídos:

  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] + '">'; console.log(template); // debugging purpose var directives = [dt2SimpleBlock]; loader.loadNextToLocation(toComponent(template, directives), elementRef); } 

E a function auxiliar para ser colocada em algum lugar como útil

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

Minha solução:

Classe ViewResolver Angular 2.0

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

Não é bem o que você pediu, mas vale a pena mencionar:

Outra solução simples, que funciona para a maioria dos casos de uso, é colocar a lógica no próprio modelo, da seguinte forma:

 @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: ` 
` }) class MyComponent { constructor() { this.loggedIn = false; } }

A desvantagem dessa solução é que o arquivo js servido acaba contendo os dois modelos, portanto, isso pode ser um problema para modelos grandes (mas apenas um modelo é realmente renderizado e a sobrecarga do tamanho js é aceitável em muitos casos).

Minha solução: (A beleza sobre isso é o carregamento lento para arquivos html e css.)

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

A diretiva que usei e fiz algumas alterações: está em home.componenet.html

  

Esta é a diretiva para componentes dynamics:

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

Atualize para a resposta do @Eyal Vardi (o ViewResolver está obsoleto):

 import { Directive, Type, Component } from '@angular/core'; import { DirectiveResolver } from '@angular/compiler'; class myViewUrlResolver extends DirectiveResolver { resolve(type: Type, throwIfNotFound?: boolean): Directive { let view = super.resolve(type, throwIfNotFound); if (typeof view["templateUrl"] !== "undefined") { console.log("Yay!"); let originalUrl = (view).templateUrl; ( view).templateUrl = environment.nativeScriptAppPrePathPrefix + originalUrl.replace(".html", ".tns.html"); } if (typeof view["styleUrls"] !== "undefined") { console.log("Yay2!"); let originalUrls = (view).styleUrls; originalUrls.forEach((originalUrl, at) => (view).styleUrls[at] = environment.nativeScriptAppPrePathPrefix + originalUrl.replace(".html", ".tns.css")); } return view; } } platformNativeScriptDynamic().bootstrapModule(AppModule,{ providers: [ { provide: DirectiveResolver, useClass: myViewUrlResolver } ] }); 

1- instalar esta biblioteca

npm i -D html-loader

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

2- No webpack.config use o html-loader para arquivos html

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

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

3- Se você usar ionic, você pode copiar webpack.config.js do caminho “node_modules/@ionic/app-scripts/config/webpack.config.js” e então adicionar o carregador de html a ele

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

4-Se você usar ionic Em package.json adicione estas linhas

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

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

5-então você pode usá-lo como abaixo

 @Component({ selector: 'page-login', // templateUrl:"./login.html" template: function(){ if(globalVariables.test==2) { return require("./login2.html") } else { return require("./login.html") } }(), }) 

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

6-Se houver erro não resolvido com a function require você pode colocá-lo no arquivo declarations.d.ts como abaixo:

declarar var requer: qualquer;

Compile seu aplicativo com 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'] }) 

Parece que esta maneira de criar modelos dynamics não estará disponível para o Angular 2 devido a questões de segurança. Infelizmente, vindo do Angular 1, meu aplicativo anterior foi dinamicamente orientado dessa maneira.

Para Angular 2 – Essa pode ser uma maneira diferente de fazer o mesmo (exemplo de link abaixo). Atualizando os arquivos html do modelo para serem componentes no aplicativo, injetando-os no (o local onde você estava tentando criar o templateUrl com uma string etc.) visualize o parâmetro do modelo do componente como elementos (usando DynamicComponentLoader).

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

Espero, exemplo github para você vai te ajudar! Existem exemplos para compilar o html dynamic. Assim, você pode carregar HTML por qualquer um dos seus serviços e, em seguida, compilá-lo.