Im Idealfall müsste ich die Vorlage meiner Komponente neu laden/rerender. Wenn es jedoch einen besseren Weg gibt, werde ich sie gerne implementieren.
Gewünschtes Verhalten:
Ich habe also eine Komponente für ein Menüelement. Wenn (in einer anderen Komponente) auf eine IBO geklickt wird (irgendeine Art von 'Client') geklickt wird, muss hinzufügen (ich verwende *ngIf
) eine neue Option im Menü, die IBO Details und eine untergeordnete Liste wäre.
IBOsNavigationElement
Komponente (Menükomponente):
@Component({
selector: '[ibos-navigation-element]',
template: `
<a href="#"><i class="fa fa-lg fa-fw fa-group"></i> <span
class="menu-item-parent">{{'IBOs' | i18n}}</span>
</a>
<ul>
<li routerLinkActive="active">
<a routerLink="/ibos/IBOsMain">{{'IBOs Main' | i18n}} {{id}}</a>
</li>
<li *ngIf="navigationList?.length > 0">
<a href="#">{{'IBO Details' | i18n}}</a>
<ul>
<li routerLinkActive="active" *ngFor="let navigatedIBO of navigationList">
<a href="#/ibos/IboDetails/{{navigatedIBO['id']}}">{{navigatedIBO['name']}}</a>
</li>
</ul>
</li>
</ul>
`
})
export class IBOsNavigationElement implements OnInit {
private navigationList = <any>[];
constructor(private navigationService: NavigationElementsService) {
this.navigationService.updateIBOsNavigation$.subscribe((navigationData) => {
this.navigationList.Push(navigationData);
log.d('I received this Navigation Data:', JSON.stringify(this.navigationList));
}
);
}
ngOnInit() {
}
}
Anfangs ist navigationList
leer []
, da der Benutzer auf keine IBO (client) geklickt hat. Sobald jedoch eine IBO angeklickt wird, wird die Liste ausgefüllt. Daher brauche ich die neue Option im Menü erscheinen.
Wenn ich mit diesem Code auf eine IBO klicke, werden das <li>
-Element und die untergeordneten Elemente erstellt, aber: Ich sehe nur das <li>
-Element.
Problem:
Die Menüoption wird generiert, aber nicht von den Layoutstilen verarbeitet. Es muss mit allen Elementen initialisiert werden, um zu wissen, wie die Menüoptionen angezeigt werden.
Ich muss die Vorlage dieser Komponente neu laden, um das Menü korrekt anzuzeigen.
HINWEIS:
Wenn ich die Vorlage ohne den *ngIf
verwende, funktioniert das gut, aber ich hätte von Anfang an eine IBO Details -Option, die keinen Sinn hat, da bei der Initialisierung keine IBO angeklickt wurde.
template: `
<a href="#"><i class="fa fa-lg fa-fw fa-group"></i> <span
class="menu-item-parent">{{'IBOs' | i18n}}</span>
</a>
<ul>
<li routerLinkActive="active">
<a routerLink="/ibos/IBOsMain">{{'IBOs Main' | i18n}} {{id}}</a>
</li>
<li>
<a href="#">{{'IBO Details' | i18n}}</a>
<ul>
<li routerLinkActive="active" *ngFor="let navigatedIBO of navigationList">
<a href="#/ibos/IboDetails/{{navigatedIBO['id']}}">{{navigatedIBO['name']}}</a>
</li>
</ul>
</li>
</ul>
`
Gewünschte Ausgabe:
Bevor Sie auf etwas klicken (in init):
Nachdem ich auf eine IBO geklickt habe (Client):
Update 1:
Um zu klären, was ich meinte mit:
Die Menüoption wird generiert, aber nicht von den Layoutstilen verarbeitet
Wenn, wird meine Menükomponente ohne den *ngIf
initialisiert:
<li>
<a href="#">{{'IBO Details' | i18n}}</a>
<ul>
<li routerLinkActive="active" *ngFor="let navigatedIBO of navigationList">
<a href="#/ibos/IboDetails/{{navigatedIBO['id']}}">{{navigatedIBO['name']}}</a>
</li>
</ul>
</li>
Dann können die Layoutstile die Menüausgabe (siehe Bilder) entsprechend dieser Struktur generieren:
<li>
<a>
<ul>
<li *ngFor>
<a>
</li>
</ul>
</li>
Fügen Sie daher das +
-Symbol und das Verhalten des Untermenüs usw. hinzu.
Wenn sie jedoch ohne alle Elemente initialisiert wird (wenn *ngIf
false
ist, werden <li>
und die untergeordneten Elemente nicht gerendert, sodass das Layout sie nicht berücksichtigt, um das Menü zu zeichnen/zu erstellen) und diese Elemente nach dem Rendering hinzugefügt werden , dann sind sie im Quellcode vorhanden, aber wir können sie nicht im Menü sehen, weil:
+
erstelltIch habe es endlich geschafft!
Mein Problem war also:
Wenn die neuen HTML-Elemente generiert werden (mit *ngIf
) , werden sie nicht angezeigt, weil sie nicht erhalten ) verarbeitet wie die anderen Menüelemente.
Also habe ich gefragt, wie ich die Vorlage mit allen 'neuen' Elementen neu laden oder rendern soll ... Aber ich habe nicht gefunden, wo ich eine Komponente oder die Vorlage einer Komponente neu laden soll. As stattdessen Ich habe die Logik, die das Menü verarbeitet, auf meine aktualisierte Vorlage angewendet.
(Wenn du die Kurzgeschichtenversion haben willst, gehe nach unten und lies die Zusammenfassung)
Also habe ich mich mit der tiefsten Logik meiner Vorlage befasst und eine Anweisung zum Rendern des Menüs erstellt:
MenuDirective (Direktive)
@Directive({
selector: '[menuDirective]'
})
export class MenuDirective implements OnInit, AfterContentInit {
constructor(private menu: ElementRef,
private router: Router,
public layoutService: LayoutService) {
this.$menu = $(this.menu.nativeElement);
}
// A lot of boring rendering of layout
ngAfterContentInit() {
this.renderSubMenus(this.$menu);
}
renderSubMenus(menuElement) {
menuElement.find('li:has(> ul)').each((i, li) => {
let $menuItem = $(li);
let $a = $menuItem.find('>a');
let sign = $('<b class="collapse-sign"><em class="fa fa-plus-square-o"/></b>');
$a.on('click', (e) => {
this.toggle($menuItem);
e.stopPropagation();
return false;
}).append(sign);
});
}
}
Hier erstelle ich also die menu-Direktive, die das Layout des Menüs gemäß den vorhandenen HTML-Elementen darstellt. Und, wie Sie sehen können, Ich habe das Verhalten, das die Menüelemente verarbeitet, isoliert Hinzufügen des Symbols +
, Erstellen der Untermenüfunktion usw. ...: renderSubMenus()
.
Wie verhält sich renderSubMenus()
:
Es durchläuft die DOM-Elemente des als Parameter übergebenen nativeElement
und wendet die Logik an, um das Menü korrekt anzuzeigen.
menu.html
<ul menuDirective>
<li ibos-navigation-element></li>
<li>
<a href="#"><i class="fa fa-lg fa-fw fa-shopping-cart"></i> <span
class="menu-item-parent">{{'Orders' | i18n}}</span></a>
<ul>
<li routerLinkActive="active">
<a routerLink="/orders/ordersMain">{{'Orders' | i18n}}</a>
</li>
</ul>
</li>
</ul>
Und so würde ich das Menü zusammenstellen.
Sehen wir uns nun die Komponente IBOsNavigationElement
an, die im Menü mit dem Attribut [ibos-navigation-element]
Enthalten ist.
IBOsNavigationElement (Komponente)
@Component({
selector: '[ibos-navigation-element]',
template: `
<a href="#"><i class="fa fa-lg fa-fw fa-group"></i> <span
class="menu-item-parent">{{'IBOs' | i18n}}</span>
</a>
<ul class="renderMe">
<li routerLinkActive="active">
<a routerLink="/ibos/IBOsMain">{{'IBOs Main' | i18n}} {{id}}</a>
</li>
<li *ngIf="navigationList?.length > 0">
<a href="#">{{'IBO Details' | i18n}}</a>
<ul>
<li routerLinkActive="active" *ngFor="let navigatedIBO of navigationList">
<a href="#/ibos/IboDetails/{{navigatedIBO['id']}}">{{navigatedIBO['name']}}</a>
</li>
</ul>
</li>
</ul>
`
})
export class IBOsNavigationElement implements OnInit, DoCheck {
private $menuElement: any;
private navigationList = <any>[];
private menuRendered: boolean = false;
constructor(private navigationService: NavigationElementsService, private menuDirective: MenuDirective, private menuElement: ElementRef) {
this.$menuElement = $(this.menuElement.nativeElement);
this.navigationService.updateIBOsNavigation$.subscribe((navigationData) => {
this.navigationList.Push(navigationData);
log.d('I received this Navigation Data:', JSON.stringify(this.navigationList));
}
);
}
ngOnInit() {
}
ngDoCheck() {
if (this.navigationList.length > 0 && !this.menuRendered) {
log.er('calling renderSubMenus()');
this.menuDirective.renderSubMenus(this.$menuElement.find('ul.renderMe'));
this.menuRendered = true;
}
}
}
Ok, was habe ich hier anders gemacht? Verschiedene Dinge...
MenuDirective
, damit ich ihre Methode renderSubMenus()
aufrufen kann.ElementRef
und find()
, um den Codeblock auszuwählen, den ich an this.menuDirective.renderSubMenus()
senden möchte. Ich finde es durch seine class
, siehe: this.$menuElement.find('ul.renderMe')
.ngDoCheck()
Methode, mit der ich überprüfe, ob die Liste navigationList
ausgefüllt ist und ob ich diesen Codeblock bereits gerendert habe (Ich hatte Probleme, weil zu oft gerendert wurde und ich hatte gerne 6 +
Tasten: Katastrophe) .Zusammenfassung:
Um die Vorlage neu zu laden :
ElementRef
bekomme ich den Teil der Vorlage, den ich 'neu laden' möchte .ngDoCheck()
gemacht. Sie können diese Methode jederzeit aufrufen.*ngIf
instanziiert hätte.Also technisch gesehen Ich habe die Komponente nicht neu geladen. Ich habe auf die Vorlage der Komponente dieselbe Logik angewendet, die angewendet worden wäre, wenn ich sie neu geladen hätte.
Angular hat zwei Änderungserkennungsstrategien:
Die Standardstrategie, die Änderungen im Modell automatisch erkennt und die Komponenten entsprechend neu rendert.
OnPush rendert die Komponente nur dann neu, wenn Sie dies ausdrücklich anweisen. Siehe auch https://angular.io/docs/ts/latest/api/core/index/ChangeDetectionStrategy-enum.html
Die OnPush-Strategie kann eine bessere Leistung erzielen, wenn Sie mehrere Elemente auf einer Seite haben oder mehr Kontrolle über den Renderprozess haben möchten.
Um diese Strategie zu verwenden, müssen Sie sie in Ihrer Komponente deklarieren:
@Component({
selector: '[ibos-navigation-element]',
template: `...`,
changeDetection: ChangeDetectionStrategy.OnPush
})
Und spritze in deinen Konstruktor:
constructor(
private changeDetector: ChangeDetectorRef,
) {}
Wenn Sie die Änderungserkennung auslösen möchten, damit die Komponente erneut gerendert werden kann (in Ihrem Fall direkt nach dem Hinzufügen eines neuen IBO/Clients zum Modell), rufen Sie Folgendes auf:
this.changeDetector.markForCheck();
Überprüfen Sie die Live-Demo aus dem offiziellen Tutorial: http://plnkr.co/edit/GC512b?p=preview
Wenn sich das Problem nicht auf die Änderungserkennung bezieht, sondern mit dem CSS/SCSS-Stil zusammenhängt, beachten Sie, dass in Angular 2 jede Komponente ihre eigenen CSS-Klassen hat und nicht "geerbt" wird. Durch die "Kinder" -Elemente sind sie vollständig voneinander isoliert. Eine Lösung könnte die Schaffung globaler CSS/SCSS-Stile sein.
Versuchen Sie es mit ChangeDetectorRef.detectChanges () - es funktioniert ähnlich wie $ scope. $ Digest () from Angular 1.
Hinweis: ChangeDetectorRef muss in die Komponente eingefügt werden.