Frage
Was ist der eleganteste Weg, @ViewChild
nachdem das entsprechende Element in der Vorlage angezeigt wurde?
Unten ist ein Beispiel. Auch Plunker erhältlich.
Vorlage:
<div id="layout" *ngIf="display">
<div #contentPlaceholder></div>
</div>
Komponente:
export class AppComponent {
display = false;
@ViewChild('contentPlaceholder', {read: ViewContainerRef}) viewContainerRef;
show() {
this.display = true;
console.log(this.viewContainerRef); // undefined
setTimeout(()=> {
console.log(this.viewContainerRef); // OK
}, 1);
}
}
Ich habe eine Komponente, deren Inhalt standardmäßig ausgeblendet ist. Wenn jemand die show()
Methode aufruft , wird sie sichtbar. Bevor jedoch die Erkennung von Angular 2-Änderungen abgeschlossen ist, kann ich nicht darauf verweisen viewContainerRef
. Normalerweise verpacke ich alle erforderlichen Aktionen setTimeout(()=>{},1)
wie oben gezeigt. Gibt es einen korrekteren Weg?
Ich weiß, dass es eine Option gibt ngAfterViewChecked
, aber sie verursacht zu viele nutzlose Anrufe.
ANTWORT (Plunker)
angular
angular2-changedetection
sinedsem
quelle
quelle
Antworten:
Verwenden Sie einen Setter für das ViewChild:
Der Setter heißt ein Element mit Bezug einmal
*ngIf
wirdtrue
.Beachten Sie, dass Sie für Angular 8 sicherstellen müssen, dass dies
{ static: false }
die Standardeinstellung in anderen Agnular-Versionen ist:Hinweis: Wenn contentPlaceholder eine Komponente ist, können Sie ElementRef in Ihre Komponentenklasse ändern:
quelle
contentPlaceholder
istElementRef
nichtViewContainerRef
.<div #contentPlaceholder></div>
. Technisch kann man es wie jeden anderen Setter manuell aufrufen,this.content = someElementRef
aber ich verstehe nicht, warum man das machen möchte.Eine Alternative, um dies zu überwinden, besteht darin, den Änderungsdetektor manuell auszuführen.
Sie injizieren zuerst die
ChangeDetectorRef
:Anschließend rufen Sie es auf, nachdem Sie die Variable aktualisiert haben, die die * ngIf steuert
quelle
onInit()
. Deshalb habe ich diedetectChanges
Funktion hinzugefügt, bevor ich eine untergeordnete Funktion aufgerufen habe, und sie wurde behoben. (Ich habe sowohl die akzeptierte Antwort als auch diese Antwort verwendet)Winkel 8+
Sie sollten
{ static: false }
als zweite Option für hinzufügen@ViewChild
. Dies führt dazu, dass die Abfrageergebnisse aufgelöst werden, nachdem die Änderungserkennung ausgeführt wurde, sodass Sie@ViewChild
nach Änderungen des Werts aktualisiert werden können.Beispiel:
Stackblitz-Beispiel: https://stackblitz.com/edit/angular-d8ezsn
quelle
<mat-horizontal-stepper *ngIf="viewMode === DialogModeEnum['New']" linear #stepper
,@ViewChild('stepper', {static: true}) private stepper: MatStepper;
undthis.changeDetector.detectChanges();
es funktioniert immer noch nicht.Die obigen Antworten haben bei mir nicht funktioniert, da sich in meinem Projekt die ngIf auf einem Eingabeelement befindet. Ich brauchte Zugriff auf das nativeElement-Attribut, um mich auf die Eingabe zu konzentrieren, wenn ngIf wahr ist. Es scheint kein nativeElement-Attribut in ViewContainerRef zu geben. Folgendes habe ich getan ( gemäß der @ ViewChild-Dokumentation ):
Ich habe setTimeout vor dem Fokussieren verwendet, da die Zuweisung des ViewChild eine Sekunde dauert. Sonst wäre es undefiniert.
quelle
setTimeout
?I usually wrap all required actions into setTimeout(()=>{},1) as shown above. Is there a more correct way?
Wie bereits von anderen erwähnt, besteht die schnellste und schnellste Lösung darin, [versteckt] anstelle von * ngIf zu verwenden. Auf diese Weise wird die Komponente erstellt, aber nicht sichtbar. Dort können Sie darauf zugreifen, obwohl dies möglicherweise nicht die effizienteste ist Weg.
quelle
Das könnte funktionieren, aber ich weiß nicht, ob es für Ihren Fall geeignet ist:
quelle
ngAfterViewInit()
stattngOnInit()
. Ich habe angenommen, dass diesviewContainerRefs
bereits initialisiert ist, aber noch keine Elemente enthält. Scheint, als hätte ich mich falsch daran erinnert.AfterViewInit
funktioniert tatsächlich. Ich habe alle meine Kommentare entfernt, um die Leute nicht zu verwirren. Hier ist ein funktionierender Plunker: plnkr.co/edit/myu7qXonmpA2hxxU3SLB?p=previewconst
Parameter vonViewChild
Ein weiterer schneller "Trick" (einfache Lösung) ist die Verwendung des [hidden] -Tags anstelle von * ngIf. Es ist nur wichtig zu wissen, dass Angular in diesem Fall das Objekt erstellt und unter der Klasse malt: hidden funktioniert die ViewChild daher problemlos . Beachten Sie daher, dass Sie keine versteckten Gegenstände für schwere oder teure Gegenstände verwenden sollten, die Leistungsprobleme verursachen können
quelle
Mein Ziel war es, alle hackigen Methoden zu vermeiden, die etwas voraussetzen (z. B. setTimeout), und am Ende implementierte ich die akzeptierte Lösung mit etwas RxJS- Flair :
Mein Szenario: Ich wollte
@ViewChild
je nach Router eine Aktion für ein Element auslösenqueryParams
. Da ein Wrapping*ngIf
falsch ist, bis die HTTP-Anforderung die Daten zurückgibt, erfolgt die Initialisierung des@ViewChild
Elements mit einer Verzögerung.So funktioniert es:
combineLatest
Gibt zum ersten Mal einen Wert aus, wenn jedes der bereitgestellten Observables den ersten Wert seit demcombineLatest
Abonnieren des Moments ausgibt. Mein BetrefftabSetInitialized
gibt einen Wert aus, wenn das@ViewChild
Element festgelegt wird. Damit verzögere ich die Ausführung des Codes unter,subscribe
bis das*ngIf
positiv wird und das@ViewChild
initialisiert wird.Vergessen Sie natürlich nicht, sich bei ngOnDestroy abzumelden. Ich mache das mit dem
ngUnsubscribe
Betreff:quelle
Als vereinfachte Version hatte ich ein ähnliches Problem wie bei der Verwendung des Google Maps JS SDK.
Meine Lösung bestand darin, das
div
undViewChild
in eine eigene untergeordnete Komponente zu extrahieren, die bei Verwendung in der übergeordneten Komponente mit einem ausgeblendet / angezeigt werden konnte*ngIf
.Vor
HomePageComponent
VorlageHomePageComponent
KomponenteNach dem
MapComponent
VorlageMapComponent
KomponenteHomePageComponent
VorlageHomePageComponent
Komponentequelle
In meinem Fall musste ich ein ganzes Modul nur laden, wenn das div in der Vorlage vorhanden war, was bedeutet, dass sich der Ausgang in einem ngif befand. Auf diese Weise wurde jedes Mal, wenn der Winkel das Element #geolocalisationOutlet erkannte, die darin enthaltene Komponente erstellt. Das Modul wird ebenfalls nur einmal geladen.
quelle
Ich denke, die Verwendung von Aufschub von Lodash ist sehr sinnvoll, besonders in meinem Fall, in dem ich mich
@ViewChild()
in einemasync
Rohr befandquelle
Arbeiten mit Angular 8 ChangeDector muss nicht importiert werden
Mit ngIf können Sie das Element nicht laden und vermeiden, dass Ihre Anwendung stärker belastet wird. Hier ist, wie ich es ohne ChangeDetector zum Laufen gebracht habe
Wenn ich dann meinen ngIf-Wert so ändere, dass er wahr ist, würde ich setTimeout wie folgt verwenden, damit es nur auf den nächsten Änderungszyklus wartet:
Dadurch konnte ich auch vermeiden, zusätzliche Bibliotheken oder Importe zu verwenden.
quelle
für Angular 8 - eine Mischung aus Null-Checking und
@ViewChild
static: false
Hackeryfür eine Paging-Steuerung, die auf asynchrone Daten wartet
quelle
Es funktioniert für mich, wenn ich ChangeDetectorRef in Angular 9 verwende
quelle