2016-03-17 15 views
33

Buduję aplikację Angular2 w Maszynopisie i chciałbym korzystać z funkcjonalności systemu klasy (czytaj: dziedziczenie klas) oferowanej przez Typescript. Wydaje się jednak, że Angular2 nie gra ładnie z klasami pochodnymi. Szukam pomocy w uruchomieniu aplikacji.Obsługa dziedziczenia Angular2 i klasy

Problem, który napotykam, polega na tym, że mam klasę bazową i wyprowadzam z niej kilka klas dla dzieci. Kiedy buduję moje drzewo komponentów, chciałbym mieć dostęp do elementów nadrzędnych/podrzędnych komponentów (tak czy inaczej jest w porządku). Z tego co rozumiem, Angular2 oferuje dwie opcje, aby to osiągnąć:

  1. wstrzyknąć rodzica do składnika dziecięcej
  2. Zastosowanie ContentChildren (lub ViewChildren), aby uzyskać dostęp dzieci komponentu.

Oba działają dobrze, jeśli znasz typ klasy, z którą pracujesz (ChildComponent), ale wydają się nie działać, gdy próbujesz użyć klasy bazowej tych komponentów (BaseComponent) jako selektora.

Do wizualizacji go w niektórych kodu (patrz ten Plunker dla demo na żywo), mam komponent aplikacji/klasa następująco:

@Component({ 
    selector: 'my-app', 
    template: `<parent-comp> 
     <child-comp1></child-comp1> 
     <child-comp1></child-comp1> 
     <child-comp2></child-comp2> 
    </parent-comp>`, 
    directives: [ParentComponent, ChildComponent1, ChildComponent2] 
}) 
export class MyApplication { 
} 

klasy bazowej i dziećmi Zajęcia są zdefiniowane jako:

export class BaseComponent { 
    // Interesting stuff here 
} 

@Component({ 
    selector: 'child-comp2', 
    template: '<div>child component #2</div>' 
}) 
export class ChildComponent2 extends BaseComponent { 
} 

@Component({ 
    selector: 'child-comp1', 
    template: '<div>child component #1</div>' 
}) 
export class ChildComponent1 extends BaseComponent { 
} 

A klasa nadrzędna ma pewną logikę do liczenia swoich dzieci.

@Component({ 
    selector: 'parent-comp', 
    template: `<div>Hello World</div> 
    <p>Number of Child Component 1 items: {{numComp1}} 
    <p>Number of Child Component 2 items: {{numComp2}} 
    <p>Number of Base Component items: {{numBase}} 
    <p><ng-content></ng-content> 
    ` 
}) 
export class ParentComponent implements AfterContentChecked { 

    @ContentChildren(ChildComponent1) contentChild1: QueryList<ChildComponent1> 
    @ContentChildren(ChildComponent2) contentChild2: QueryList<ChildComponent2> 
    @ContentChildren(BaseComponent) contentBase: QueryList<BaseComponent> 
    public numComp1:number 
    public numComp2:number 
    public numBase:number 

    ngAfterContentChecked() { 
    this.numComp1 = this.contentChild1.length 
    this.numComp2 = this.contentChild2.length 
    this.numBase = this.contentBase.length 
    } 

(Ponownie, można zobaczyć na żywo pokaz here)

Wyjście dla pierwszych dwóch liczników jest zgodnie z oczekiwaniami. Istnieją 2 dzieci ChildComponent1 i 1 ChildComponent2. Niestety, licznik BaseComponent nie pokazuje sumy tych liczników, ale pokazuje 0. Nie znajduje żadnej klasy, która jest typu BaseComponent w dzieciach.

To samo dzieje się, gdy ParentComponent rozciąga się również od BaseComponent i chcesz wprowadzić go do ChildComponent. Injector będzie wymagał określonego typu ParentComponent i nie będzie mógł pracować z klasą podstawową.

Jakieś wskazówki dotyczące pracy z klasami pochodnymi w Angular2? Czy brakuje mi czegoś lub próbuję czegoś niemożliwego?

+0

myślę, że to to problem kątowy, a dokładniej z implementacją ContentChildren i ViewChildren, ponieważ jak rozumiem, używają tylko metadanych dziecka, aby dokonać porównania, tak długo jak nie jest uwzględniony element BaseComponent, dziecko nie jest przeniesiony do QueryList. Jeśli jednak dodasz dostawców: [BaseComponent] do swoich dzieci, w metadanych składnika pojawi się instancja BaseComponent, a bieżące potomstwo zostanie dodane do QueryList EVEN, jeśli dziedziczenie nie jest używane, a także utworzy INNE wystąpienie składnika BaseComponent innego niż dziecko. –

+0

[Link Plunker] (http://plnkr.co/edit/dpdJvK06OY3o44pWiAMG?p=preview) dla przypadku opisanego w poprzednim komentarzu. –

+0

@NikolaNikolov: Wypróbowałem twoją sugestię, mając BaseComponent jako dostawcę. To rzeczywiście dodaje BaseComponent do listy ContentChildren jednostki nadrzędnej. Jednak te instancje są, jak już wspomniano, inne niż same instancje klas pochodnych. Szkoda. – Sjoerd

Odpowiedz

32

Dodaj dostawcę do każdego składnika pochodzącego, aliasing składnik bazowy składnik pochodne:

@Component({ 
    selector: 'child-comp2', 
    template: '<div>child component #2</div>', 
    providers: [{provide: BaseComponent, useExisting: forwardRef(() => ChildComponent2) }] 
}) 
export class ChildComponent2 extends BaseComponent { 
} 

@Component({ 
    selector: 'child-comp1', 
    template: '<div>child component #1</div>', 
    providers: [{provide: BaseComponent, useExisting: forwardRef(() => ChildComponent1) }] 
}) 
export class ChildComponent1 extends BaseComponent { 
} 

Plunker: http://plnkr.co/edit/5gb5E4curAE2EfH2lNZQ?p=preview

+0

Dzięki, to wydaje się działać! Sprawdziłem również, czy rzeczywiście otrzymasz instancję pochodną, ​​a nie osobną instancję komponentu, która również wydaje się działać poprawnie: http://plnkr.co/edit/KSchL5docebxEQU692u9?p=preview – Sjoerd

+0

w jaki sposób obsłużyć zależności od BaseClass w konstruktorze? –

+0

@TobiasGassmann: Powinieneś opublikować nowe pytanie z konkretnym przykładem. –

0

można użyć zmiennych lokalnych szablonów z kwerendy ContentChildren utworzyć listę wszystkich komponentów ze wspólną klasą podstawową. Oto kod plunk dla kodu poniżej. Takie podejście zostało zaczerpnięte z sugestii zamieszczonej w blogu Kara Erickson: Rookie mistakes.

W szablonie każdy składnik dziecko oznaczonego #child:

<app-container> 
    <app-child1 #child></app-child1> 
    <app-child1 #child></app-child1> 
    <app-child1 #child></app-child1> 
    <app-child1 #child></app-child1> 
    <app-child2 #child></app-child2> 
    </app-container> 

ten może być następnie używany, aby zaznaczyć wszystkie elementy z udostępnionej składnika podstawowego:

import { Component, QueryList, ContentChildren, ViewChildren, AfterViewInit, AfterContentInit } from '@angular/core'; 
    import { BaseComponent } from '../base/base.component'; 
    import { Child1Component } from '../child1/child1.component'; 
    import { Child2Component } from '../child2/child2.component'; 

    @Component({ 
    selector: 'app-container', 
    templateUrl: './src/container/container.component.html' 
    }) 
    export class ContainerComponent implements AfterContentInit { 
    @ContentChildren('child') allChildren: QueryList<BaseComponent>; 
    @ContentChildren(Child1Component) child1Chdilren: QueryList<Child1Component>; 
    @ContentChildren(Child2Component) child2Chdilren: QueryList<Child2Component>; 

    constructor() { } 

    ngOnInit() { 
    } 

    ngAfterContentInit() { 
    } 

    } 
Powiązane problemy