Próbuję nauczyć się Angular 2.
Chciałbym uzyskać dostęp do komponentu podrzędnego z komponentu nadrzędnego za pomocą adnotacji @ViewChild .
Oto kilka wierszy kodu:
W BodyContent.ts mam:
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);
}
}
podczas gdy w 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(){};
}
Wreszcie tutaj szablony (zgodnie z sugestiami w komentarzach):
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>
Szablon FilterTiles.html jest poprawnie załadowany do tagu ico-filter-tiles (rzeczywiście jestem w stanie zobaczyć nagłówek).
Uwaga: klasa BodyContent jest wstrzykiwana do innego szablonu (Body) przy użyciu DynamicComponetLoader: dcl.loadAsRoot (BodyContent, '# ico-bodyContent', inżektor):
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);
}
}
Problem polega na tym, że kiedy próbuję pisać ft
do dziennika konsoli, dostaję undefined
i oczywiście dostaję wyjątek, gdy próbuję wepchnąć coś do tablicy „kafelków”: „brak kafelków właściwości dla„ niezdefiniowany ”” .
Jeszcze jedno: składnik FilterTiles wydaje się być poprawnie załadowany, ponieważ widzę dla niego szablon HTML.
Jakieś sugestie? Dzięki
źródło
ft
nie byłby ustawiony w konstruktorze, ale w module obsługi zdarzeń kliknięcia byłby już ustawiony.loadAsRoot
, który ma znany problem z wykrywaniem zmian. Tylko dla pewności spróbuj użyćloadNextToLocation
lubloadIntoLocation
.loadAsRoot
. Po zastąpieniuloadIntoLocation
problem został rozwiązany. Jeśli podasz komentarz jako odpowiedź,Odpowiedzi:
Miałem podobny problem i pomyślałem, że opublikuję na wypadek, gdyby ktoś popełnił ten sam błąd. Po pierwsze, jedną rzeczą do rozważenia jest
AfterViewInit
; musisz poczekać na zainicjowanie widoku, zanim będziesz mieć dostęp do swojego@ViewChild
. Jednak mój@ViewChild
wciąż zwracał wartość zero. Problem był mój*ngIf
.*ngIf
Dyrektywa zabija moje komponencie kontroli, więc nie mogłem go odwołać.Mam nadzieję, że to pomaga.
EDYCJA
Jak wspomniano poniżej przez @Ashg , rozwiązaniem jest użycie
@ViewChildren
zamiast@ViewChild
.źródło
ngClass
+hidden
klasyngIf
. To się udało. Dzięki!Jak już wspomniano, problemem jest
ngIf
niezdefiniowanie widoku. Odpowiedź brzmi: użyjViewChildren
zamiastViewChild
. Miałem podobny problem, w którym nie chciałem pokazywać siatki, dopóki wszystkie dane referencyjne nie zostaną załadowane.HTML:
Kod części
Tutaj używamy,
ViewChildren
na których można nasłuchiwać zmian. W tym przypadku wszystkie dzieci z referencją#searchGrid
. Mam nadzieję że to pomoże.źródło
this.SearchGrid
właściwości, których należy używać, takich jaksetTimeout(()=>{ ///your code here }, 1);
unikanie Wyjątek: Wyrażenie zmieniło się po sprawdzeniu*ngIf
działa, a po renderowaniu możemy zapisać ElementRef z komponentów dynamicznych.Możesz użyć setera dla
@ViewChild()
Jeśli masz otoki ngIf, setter zostanie wywołany z niezdefiniowanym, a następnie ponownie z referencją, gdy ngIf pozwoli na renderowanie.
Mój problem był jednak inny. W moim app.modules nie zawarłem modułu zawierającego moje „FilterTiles”. Szablon nie zgłosił błędu, ale odwołanie zawsze było niezdefiniowane.
źródło
FilterTiles
. Z tego powodu napotkałem już wcześniej ten problem.@ViewChild('paginator', {static: false})
To zadziałało dla mnie.
Na przykład mój komponent o nazwie „mój-komponent” został wyświetlony za pomocą * ngIf = „showMe” w następujący sposób:
Tak więc po zainicjowaniu komponentu komponent nie jest jeszcze wyświetlany, dopóki „showMe” nie będzie prawdziwe. Tak więc wszystkie moje referencje @ViewChild były niezdefiniowane.
To tutaj użyłem @ViewChildren i zwróconej listy QueryList. Zobacz ciekawy artykuł na temat QueryList i demo użytkowania @ViewChildren .
Możesz użyć QueryList, którą zwraca @ViewChildren i subskrybować wszelkie zmiany w odnośnych elementach za pomocą rxjs, jak pokazano poniżej. @ViewChild nie ma tej zdolności.
Mam nadzieję, że to pomoże komuś zaoszczędzić trochę czasu i frustracji.
źródło
.take(1).subscribe()
, ale doskonała odpowiedź, dziękuję bardzo!Moim obejściem było użycie
[style.display]="getControlsOnStyleDisplay()"
zamiast*ngIf="controlsOn"
. Blok jest tam, ale nie jest wyświetlany.źródło
Tym, co rozwiązało mój problem, było upewnienie się, że
static
zostało ustawionefalse
.Po
static
wyłączeniu@ViewChild
odniesienie jest aktualizowane przez Angular po*ngIf
zmianie dyrektywy.źródło
Moje rozwiązanie to było zastąpienie
*ngIf
z[hidden]
. Minusem było to, że wszystkie elementy potomne były obecne w kodzie DOM. Ale pracował dla moich wymagań.źródło
W moim przypadku miałem ustawiający zmienną wejściową za pomocą
ViewChild
, iViewChild
znajdował się w*ngIf
dyrektywie, więc ustawiający próbował uzyskać do niego dostęp przed*ngIf
renderowanym (działałby dobrze bez*ngIf
, ale nie działałby, gdyby zawsze był ustawiony na prawda z*ngIf="true"
).Aby rozwiązać, użyłem Rxjs, aby upewnić się, że wszelkie odniesienia do
ViewChild
oczekiwania czekały na zainicjowanie widoku. Najpierw utwórz Temat, który zostanie ukończony, gdy zostanie wyświetlony init.Następnie utwórz funkcję, która pobiera i wykonuje lambda po zakończeniu tematu.
Na koniec upewnij się, że odwołania do ViewChild korzystają z tej funkcji.
źródło
To musi działać.
Ale jak powiedział Günter Zöchbauer , w szablonie musi być jakiś inny problem. Stworzyłem coś w rodzaju Relevant-Plunkr-Answer . Proszę sprawdzić konsolę przeglądarki.
boot.ts
filterTiles.ts
To działa jak urok. Sprawdź dokładnie swoje tagi i referencje.
Dzięki...
źródło
To działa dla mnie, patrz przykład poniżej.
źródło
Miałem podobny problem, w którym
ViewChild
znajdowała się wswitch
klauzuli, która nie ładowała elementu viewChild przed odwołaniem się do niego. Rozwiązałem go w pół-hacky sposób, ale zawijającViewChild
odwołanie wsetTimeout
wykonywanym natychmiast (tj. 0ms)źródło
Moim rozwiązaniem tego było przeniesienie ngIf z zewnątrz komponentu potomnego do wnętrza komponentu potomnego na div, który owinął całą sekcję HTML. W ten sposób wciąż się ukrywał, kiedy trzeba, ale był w stanie załadować komponent i mogłem odwoływać się do niego w rodzicu.
źródło
Naprawiam to po prostu dodając SetTimeout po ustawieniu widocznego komponentu
Mój HTML:
My Component JS
źródło
W moim przypadku wiedziałem, że element potomny będzie zawsze obecny, ale chciałem zmienić stan przed zainicjowaniem go przez dziecko w celu zaoszczędzenia pracy.
Wybieram testowanie dziecka, aż się pojawi, i natychmiast wprowadzam zmiany, co zapisało mi cykl zmian w elemencie potomnym.
Ostrzegawczo, możesz dodać maksymalną liczbę prób lub dodać kilka ms opóźnienia do
setTimeout
.setTimeout
skutecznie rzuca funkcję na dół listy oczekujących operacji.źródło
Rodzaj ogólnego podejścia:
Możesz utworzyć metodę, która będzie czekać, aż
ViewChild
będzie gotowaStosowanie:
źródło
Dla mnie problem polegał na tym, że odwoływałem się do identyfikatora elementu.
Zamiast tego:
źródło
Jeśli używasz Ionic, musisz użyć
ionViewDidEnter()
haka cyklu życia. Ionic biegnie kilka dodatkowych rzeczy (głównie związane animacji), które zazwyczaj powoduje nieoczekiwane błędy jak ten, stąd potrzeba czegoś, co biegnie pongOnInit
,ngAfterContentInit
i tak dalej.źródło
Oto coś, co zadziałało dla mnie.
Zasadniczo więc sprawdzam co sekundę, aż stanie
*ngIf
się to prawdą, a następnie robię swoje rzeczy związane zElementRef
.źródło
Rozwiązaniem, które działało dla mnie, było dodanie dyrektywy do deklaracji w app.module.ts
źródło