Ich habe in den letzten Tagen mit Angular 2 herumgespielt und mich gefragt, ob es möglich ist templateUrl
, dem @View
Dekorateur eine Dynamik zu verleihen.
Ich habe versucht, ihm eine Funktion zu übergeben und einen String daraus zurückzugeben, aber die gesamte Funktion wird einfach in einen String umgewandelt.
Ich habe Angular 1.x vorher auch nicht wirklich verwendet, daher weiß ich nicht, ob ich dies nur falsch mache, aber ist dies möglich oder gibt es eine bessere Möglichkeit, dynamische Ansichten zu erstellen?
Zum Beispiel möchte ich möglicherweise ein Formular anzeigen, wenn der Benutzer nicht angemeldet ist, aber eine Textnachricht anzeigen, wenn er angemeldet ist.
So etwas funktioniert nicht:
@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;
}
}
Jede Hilfe wäre dankbar.
javascript
angular
Lucas
quelle
quelle
More than one @View for a component
.Antworten:
Obwohl dies möglicherweise nicht die eleganteste Lösung ist, habe ich DynamicComponentLoader und ElementRef verwendet, um einer Komponente dynamisch einen Vorlagenwert zuzuweisen. Tatsächlich suchte ich nach einer Lösung, mit der ich einem Platzhalter mehrere benutzerdefinierte Komponenten hinzufügen kann.
Ich habe versucht, die Dienstinjektion in die von shmck beschriebene Funktion einzufügen. Dies funktioniert nicht, da die Dienste beim Aufrufen der Vorlagenfunktion noch nicht verfügbar sind.
this
Bezieht sich in der Tat auf das Fensterobjekt.Referenz-URLs für die von mir verwendete Lösung finden Sie unter: Erstellen Sie dynamische Ankernamen / Komponenten mit ComponentResolver und ngFor in Angular2
Ich beziehe mich auch auf Plnkr1 und Plnkr2 .
Die Site Dartdocs bietet eine schöne Dokumentation zur Angular 2 DynamicComponentLoader-Klasse, die auch für TypeScript gilt.
Zusamenfassend:
Eine einfache Komponente als zu verwendende Vorlage
@Component({ selector: 'dt2-simple-block', properties: ["idx"], template: `<h1>Simple block for {{ idx }} </h1>`, directives: [] }) class dt2SimpleBlock { constructor() { } }
Konstruktor der Komponente, die alle hinzuzufügenden Komponenten enthält (für meine App müssen mehrere untergeordnete Komponenten enthalten sein:
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); }
Und die Hilfsfunktion soll irgendwo als util platziert werden
function toComponent(template, directives = []) { @Component({ selector: 'fake-component' }) @View({ template, directives }) class FakeComponent { } return FakeComponent; }
quelle
{template, directives}
?{template: template, directives: directives}
.DynamicComponentLoader
ist veraltet. angular.io/docs/ts/latest/api/core/index/…Meine Lösung:
Angular 2.0 ViewResolver-Klasse
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}) ]);
quelle
DirectiveResolver
DirectiveResolver
die entfernt wurde?Nicht ganz das, wonach Sie gefragt haben, aber es ist erwähnenswert:
Eine andere einfache Lösung, die für die meisten Anwendungsfälle funktioniert, besteht darin, die Logik wie folgt in die Vorlage selbst einzufügen:
@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; } }
Der Nachteil dieser Lösung ist, dass Ihre bereitgestellte JS-Datei letztendlich beide Vorlagen enthält. Dies kann daher ein Problem für große Vorlagen sein (es wird jedoch nur eine Vorlage gerendert, und der Overhead für die JS-Größe ist in vielen Fällen akzeptabel).
quelle
Meine Lösung: (Das Schöne daran ist das verzögerte Laden von HTML- und CSS-Dateien.)
Dies ist 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'; }); } }
Die Direktive, die ich verwendet und einige Änderungen vorgenommen habe: Dies ist in home.componenet.html
<dynamic-html-outlet [htmlPath]="html" [cssPath]="css"></dynamic-html-outlet>
Dies ist die Direktive für dynamische Komponenten:
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, []); }); } }
quelle
Update auf die Antwort von @Eyal Vardi (
ViewResolver
veraltet):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 } ] });
quelle
Kompilieren Sie Ihre Anwendung mit 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'] })
quelle
'2'
hier? Besiegt es nicht den Punkt der OP-Frage? Wenn ich beispielsweise einen Dienst habe, der zurückgibt'2'
, wie kann ich diese Methode dann (aus der Komponentenklasse) aufrufen?Es scheint, dass Angular 2 diese Art der Erstellung dynamischer Vorlagen aus Sicherheitsgründen nicht zur Verfügung steht. Leider wurde meine vorherige Anwendung aus Angular 1 dynamisch auf diese Weise gesteuert.
Für Winkel 2 - Dies kann eine andere Möglichkeit sein, dasselbe zu tun (Linkbeispiel unten). Durch Aktualisieren der HTML-Vorlagendateien als Komponenten in der Anwendung und anschließendes Einfügen in (den Ort, an dem Sie versucht haben, die templateUrl mit einer Zeichenfolge usw. zu erstellen) wird der Komponentenvorlagenparameter als Elemente angezeigt (mithilfe von DynamicComponentLoader).
https://angular.io/docs/js/latest/api/core/DynamicComponentLoader-class.html
quelle
Hoffe, Github Beispiel für Sie wird Ihnen helfen! Es gibt Beispiele zum Kompilieren von dynamischem HTML. Sie können also HTML von jedem Ihrer Dienste laden und dann kompilieren.
quelle
1- Installieren Sie diese Bibliothek
npm i -D HTML-Loader
================================================== ==========
2- Verwenden Sie in webpack.config den HTML-Loader für HTML-Dateien
{ test: /\.html$/, loaders: ['html-loader'] }
================================================== ==========
3- Wenn Sie ionic verwenden, können Sie webpack.config.js aus dem Pfad "node_modules/@ionic/app-scripts/config/webpack.config.js" kopieren und dann den HTML-Loader hinzufügen
================================================== ===========
4-Wenn Sie ionic In package.json verwenden, fügen Sie diese Zeilen hinzu
"config": { "ionic_bundler": "webpack", "ionic_webpack": "webpack.config.ionic.js" },
================================================== ===========
5-Dann können Sie es wie folgt verwenden
@Component({ selector: 'page-login', // templateUrl:"./login.html" template: function(){ if(globalVariables.test==2) { return require("./login2.html") } else { return require("./login.html") } }(), })
======================================
6-Wenn ein ungelöster Fehler mit der Funktion require vorliegt, können Sie ihn wie folgt in die Datei declarations.d.ts einfügen:
deklariere var require: any;
quelle
Ich weiß, dass dies die gestellte Frage technisch nicht beantwortet, aber in vielen Fällen können Sie den gewünschten Effekt erzielen, indem Sie eine neue Komponente erstellen, die die beabsichtigte Komponente erweitert und eine andere verwendet
templateUrl
. Verwenden Sie dann*ngIf
in der übergeordneten Komponente, um die richtige Vorlage zu laden.Komponente mit Vorlage 1:
@Component({ selector: 'template-one-component', templateUrl: './template-one.html' }) export class TemplateOneComponent { title = 'This component uses one template'; }
Komponente mit Vorlage 2:
@Component({ selector: 'template-two-component', templateUrl: './template-two.html' }) export class TemplateTwoComponent extends TemplateOneComponent { }
Übergeordnete Komponente:
@Component({ selector: 'parent-component', template: ` <template-one-component *ngIf="useTemplateOne; else useTemplateTwo"></template-one-component> <ng-template #useTemplateTwo> <template-two-component></template-two-component> <ng-template> ` }) export class ParentComponent { useTemplateOne: boolean; }
quelle