Ich versuche Angular 2 zu lernen.
Ich möchte mit der @ ViewChild- Annotation von einer übergeordneten Komponente aus auf eine untergeordnete Komponente zugreifen .
Hier einige Codezeilen:
In BodyContent.ts habe ich:
import {ViewChild, Component, Injectable} from 'angular2/core';
import {FilterTiles} from '../Components/FilterTiles/FilterTiles';
@Component({
selector: 'ico-body-content'
, templateUrl: 'App/Pages/Filters/BodyContent/BodyContent.html'
, directives: [FilterTiles]
})
export class BodyContent {
@ViewChild(FilterTiles) ft:FilterTiles;
public onClickSidebar(clickedElement: string) {
console.log(this.ft);
var startingFilter = {
title: 'cognomi',
values: [
'griffin'
, 'simpson'
]}
this.ft.tiles.push(startingFilter);
}
}
während in FilterTiles.ts :
import {Component} from 'angular2/core';
@Component({
selector: 'ico-filter-tiles'
,templateUrl: 'App/Pages/Filters/Components/FilterTiles/FilterTiles.html'
})
export class FilterTiles {
public tiles = [];
public constructor(){};
}
Zum Schluss hier die Vorlagen (wie in den Kommentaren vorgeschlagen):
BodyContent.html
<div (click)="onClickSidebar()" class="row" style="height:200px; background-color:red;">
<ico-filter-tiles></ico-filter-tiles>
</div>
FilterTiles.html
<h1>Tiles loaded</h1>
<div *ngFor="#tile of tiles" class="col-md-4">
... stuff ...
</div>
Die Vorlage FilterTiles.html wurde korrekt in das Tag ico-filter-tiles geladen (tatsächlich kann ich den Header sehen).
Hinweis: Die BodyContent-Klasse wird mit DynamicComponetLoader in eine andere Vorlage (Body) eingefügt: dcl.loadAsRoot (BodyContent, '# ico-bodyContent', Injector):
import {ViewChild, Component, DynamicComponentLoader, Injector} from 'angular2/core';
import {Body} from '../../Layout/Dashboard/Body/Body';
import {BodyContent} from './BodyContent/BodyContent';
@Component({
selector: 'filters'
, templateUrl: 'App/Pages/Filters/Filters.html'
, directives: [Body, Sidebar, Navbar]
})
export class Filters {
constructor(dcl: DynamicComponentLoader, injector: Injector) {
dcl.loadAsRoot(BodyContent, '#ico-bodyContent', injector);
dcl.loadAsRoot(SidebarContent, '#ico-sidebarContent', injector);
}
}
Das Problem ist, dass ich beim Versuch, ft
in das Konsolenprotokoll zu schreiben undefined
, eine Ausnahme erhalte , wenn ich versuche, etwas in das Array "tiles" zu verschieben: 'no property tiles for "undefined"' .
Noch etwas: Die FilterTiles-Komponente scheint korrekt geladen zu sein, da ich die HTML-Vorlage dafür sehen kann.
Irgendein Vorschlag? Vielen Dank
quelle
ft
würde nicht im Konstruktor festgelegt werden, aber in einem Click-Event-Handler wäre es bereits festgelegt.loadAsRoot
, was ein bekanntes Problem mit der Änderungserkennung hat. Nur um sicherzugehen, versuchen Sie es mitloadNextToLocation
oderloadIntoLocation
.loadAsRoot
. Einmal habe ich durchloadIntoLocation
das Problem ersetzt wurde gelöst. Wenn Sie Ihren Kommentar als AntwortAntworten:
Ich hatte ein ähnliches Problem und dachte, ich würde posten, falls jemand anderes den gleichen Fehler machen sollte. Erstens ist eine Sache zu berücksichtigen
AfterViewInit
; Sie müssen warten, bis die Ansicht initialisiert wurde, bevor Sie auf Ihre zugreifen können@ViewChild
. Meiner gab@ViewChild
jedoch immer noch null zurück. Das Problem war mein*ngIf
. Die*ngIf
Anweisung hat meine Steuerelementkomponente beendet, sodass ich nicht darauf verweisen konnte.Hoffentlich hilft das.
EDIT
Wie erwähnt @Ashg unten , ist eine Lösung zu verwenden ,
@ViewChildren
statt@ViewChild
.quelle
ngClass
+hidden
Klasse verwendetngIf
. Das hat funktioniert. Vielen Dank!Wie bereits erwähnt, ist das Problem das,
ngIf
was dazu führt, dass die Ansicht undefiniert ist. Die Antwort ist,ViewChildren
statt zu verwendenViewChild
. Ich hatte ein ähnliches Problem, bei dem ich nicht wollte, dass ein Raster angezeigt wird, bis alle Referenzdaten geladen wurden.html:
Komponentencode
Hier verwenden wir,
ViewChildren
auf die Sie auf Änderungen warten können. In diesem Fall alle Kinder mit der Referenz#searchGrid
. Hoffe das hilft.quelle
this.SearchGrid
Eigenschaften, die Sie Syntax verwenden sollten, umsetTimeout(()=>{ ///your code here }, 1);
zu vermeiden Ausnahme: Ausdruck hat sich geändert, nachdem es überprüft wurde*ngIf
funktioniert es, und nach dem Rendern können wir ein ElementRef aus den Dinamic-Komponenten speichern.Sie könnten einen Setter für verwenden
@ViewChild()
Wenn Sie einen ngIf-Wrapper haben, wird der Setter mit undefined und dann erneut mit einer Referenz aufgerufen, sobald ngIf das Rendern zulässt.
Mein Problem war jedoch etwas anderes. Ich hatte das Modul mit meinen "FilterTiles" nicht in meine app.modules aufgenommen. Die Vorlage hat keinen Fehler ausgegeben, aber die Referenz war immer undefiniert.
quelle
FilterTiles
. Ich bin aus diesem Grund schon einmal auf dieses Problem gestoßen.@ViewChild('paginator', {static: false})
Das hat bei mir funktioniert.
Meine Komponente mit dem Namen 'my-component' wurde beispielsweise mit * ngIf = "showMe" wie folgt angezeigt:
Wenn die Komponente initialisiert wird, wird sie erst angezeigt, wenn "showMe" wahr ist. Daher waren meine @ ViewChild-Referenzen alle undefiniert.
Hier habe ich @ViewChildren und die zurückgegebene QueryList verwendet. Siehe eckigen Artikel über QueryList und eine @ ViewChildren-Verwendungsdemo .
Sie können die von @ViewChildren zurückgegebene QueryList verwenden und alle Änderungen an den referenzierten Elementen mithilfe von rxjs abonnieren (siehe unten). @ViewChild hat diese Fähigkeit nicht.
Hoffe, das hilft jemandem, Zeit und Frust zu sparen.
quelle
.take(1).subscribe()
, aber eine ausgezeichnete Antwort, vielen Dank!Meine Problemumgehung bestand darin,
[style.display]="getControlsOnStyleDisplay()"
statt zu verwenden*ngIf="controlsOn"
. Der Block ist vorhanden, wird jedoch nicht angezeigt.quelle
Meine Lösung war zu ersetzen
*ngIf
mit[hidden]
. Nachteil war, dass alle untergeordneten Komponenten im Code-DOM vorhanden waren. Aber für meine Anforderungen gearbeitet.quelle
Was mein Problem löste, war sicherzustellen, dass
static
eingestellt warfalse
.Mit
static
ausgeschaltet wird, das@ViewChild
wird Bezug von Angular aktualisiert , wenn die*ngIf
Richtlinie ändert.quelle
In meinem Fall hatte ich einen Eingabevariablen-Setter, der das verwendete
ViewChild
, und dasViewChild
befand sich innerhalb einer*ngIf
Direktive, sodass der Setter versuchte, vor dem*ngIf
Rendern darauf zuzugreifen (es würde ohne das gut funktionieren*ngIf
, würde aber nicht funktionieren, wenn es immer auf gesetzt wäre wahr mit*ngIf="true"
).Zum Lösen habe ich Rxjs verwendet, um sicherzustellen, dass auf die Wartezeit verwiesen wird,
ViewChild
bis die Ansicht initiiert wurde. Erstellen Sie zunächst einen Betreff, der nach dem Start der Ansicht abgeschlossen wird.Erstellen Sie dann eine Funktion, die nach Abschluss des Betreffs ein Lambda übernimmt und ausführt.
Stellen Sie schließlich sicher, dass Verweise auf ViewChild diese Funktion verwenden.
quelle
Es muss funktionieren.
Aber wie Günter Zöchbauer sagte, muss es ein anderes Problem in der Vorlage geben. Ich habe irgendwie Relevant-Plunkr-Answer erstellt . Bitte überprüfen Sie die Konsole des Browsers.
boot.ts
filterTiles.ts
Es wirkt wie ein Zauber. Bitte überprüfen Sie Ihre Tags und Referenzen.
Vielen Dank...
quelle
Das funktioniert bei mir, siehe Beispiel unten.
quelle
Ich hatte ein ähnliches Problem, bei dem
ViewChild
sich das in einerswitch
Klausel befand, die das viewChild-Element nicht geladen hat, bevor auf es verwiesen wurde. Ich habe es auf eine halb-hackige Art und Weise gelöst, aber dieViewChild
Referenz in einesetTimeout
, die sofort ausgeführt wurde (dh 0ms), eingewickelt.quelle
Meine Lösung bestand darin, die ngIf von außerhalb der untergeordneten Komponente in die untergeordnete Komponente eines Div zu verschieben, das den gesamten HTML-Abschnitt umhüllte. Auf diese Weise wurde es immer noch versteckt, wenn es sein musste, aber ich konnte die Komponente laden und ich konnte sie im übergeordneten Element referenzieren.
quelle
Ich behebe es, indem ich SetTimeout hinzufüge, nachdem die Komponente sichtbar gemacht wurde
Mein HTML:
Meine Komponente JS
quelle
In meinem Fall wusste ich, dass die untergeordnete Komponente immer vorhanden sein würde, wollte jedoch den Status vor der Initialisierung des untergeordneten Elements ändern, um Arbeit zu sparen.
Ich teste das Kind, bis es angezeigt wird, und nehme sofort Änderungen vor, wodurch mir ein Änderungszyklus für die untergeordnete Komponente erspart wurde.
Sie können eine maximale Anzahl von Versuchen oder eine Verzögerung von einigen ms hinzufügen
setTimeout
.setTimeout
Wirft die Funktion effektiv an den unteren Rand der Liste der ausstehenden Vorgänge.quelle
Eine Art generischer Ansatz:
Sie können eine Methode erstellen, die wartet, bis
ViewChild
sie bereit istVerwendung:
quelle
Für mich war das Problem, dass ich auf die ID des Elements verwies.
Statt so:
quelle
Wenn Sie Ionic verwenden, müssen Sie den
ionViewDidEnter()
Lifecycle-Hook verwenden. Ionic läuft einige zusätzliche Sachen (vor allem Animationen bezogen) , die typischerweise unerwartete Fehler wie dies verursacht, daher die Notwendigkeit für etwas , das läuft nachngOnInit
,ngAfterContentInit
und so weiter.quelle
Hier ist etwas, das für mich funktioniert hat.
Also setze ich im Grunde jede Sekunde einen Scheck, bis das
*ngIf
wahr wird, und dann mache ich meine Sachen im Zusammenhang mit demElementRef
.quelle
Die Lösung, die für mich funktioniert hat, bestand darin, die Direktive in Deklarationen in app.module.ts einzufügen
quelle