Chciałbym stworzyć rozszerzenia dla niektórych komponentów już wdrożonych w Angular 2, bez konieczności ich prawie całkowitego przepisywania, ponieważ komponent podstawowy mógłby podlegać zmianom i chciałbym, aby te zmiany również znalazły odzwierciedlenie w komponentach pochodnych.
Stworzyłem ten prosty przykład, aby lepiej wyjaśnić moje pytania:
Z następującym składnikiem podstawowym app/base-panel.component.ts
:
import {Component, Input} from 'angular2/core';
@Component({
selector: 'base-panel',
template: '<div class="panel" [style.background-color]="color" (click)="onClick($event)">{{content}}</div>',
styles: [`
.panel{
padding: 50px;
}
`]
})
export class BasePanelComponent {
@Input() content: string;
color: string = "red";
onClick(event){
console.log("Click color: " + this.color);
}
}
Czy chciałbyś stworzyć inny składnik pochodny tylko zmienić np. Podstawowe zachowanie komponentu w przypadku przykładowego koloru app/my-panel.component.ts
:
import {Component} from 'angular2/core';
import {BasePanelComponent} from './base-panel.component'
@Component({
selector: 'my-panel',
template: '<div class="panel" [style.background-color]="color" (click)="onClick($event)">{{content}}</div>',
styles: [`
.panel{
padding: 50px;
}
`]
})
export class MyPanelComponent extends BasePanelComponent{
constructor() {
super();
this.color = "blue";
}
}
Kompletny przykład roboczy w Plunker
Uwaga: Oczywiście ten przykład jest prosty i mógłby zostać rozwiązany w przeciwnym razie nie ma potrzeby używania dziedziczenia, ale ma on jedynie zilustrować rzeczywisty problem.
Jak widać w implementacji składnika pochodnego app/my-panel.component.ts
, większość implementacji została powtórzona, a pojedyncza część naprawdę odziedziczyła class
BasePanelComponent
, ale w @Component
zasadzie musiała zostać całkowicie powtórzona, a nie tylko zmienione części, ponieważ selector: 'my-panel'
.
Czy jest jakiś sposób na dosłownie pełne dziedziczenie komponentu Angular2, dziedzicząc na przykład class
definicję oznaczeń / adnotacji @Component
?
Edycja 1 - żądanie funkcji
Żądanie funkcji angular2 dodane do projektu na GitHub: Rozszerz / dziedzicz adnotacje komponentów angular2 # 7968
Edycja 2 - żądanie zamknięte
Prośba została zamknięta, z tego powodu , że na krótko nie wiedziałem, jak scalić dekorator zostanie wykonany. Zostawiając nas bez opcji. Tak więc moja opinia jest cytowana w numerze .
źródło
Odpowiedzi:
Alternatywne rozwiązanie:
Ta odpowiedź Thierry'ego Templiera jest alternatywnym sposobem obejścia problemu.
Po kilku pytaniach z Thierrym Templierem doszedłem do następującego przykładu roboczego, który spełnia moje oczekiwania jako alternatywa dla ograniczenia dziedziczenia wspomnianego w tym pytaniu:
1 - Utwórz niestandardowy dekorator:
2 - Komponent podstawowy z dekoratorem @Component:
3 - Składnik podrzędny z dekoratorem @CustomComponent:
Plunkr z pełnym przykładem.
źródło
Angular 2 w wersji 2.3 został właśnie wydany i zawiera natywne dziedziczenie komponentów. Wygląda na to, że możesz dziedziczyć i zastępować, co chcesz, z wyjątkiem szablonów i stylów. Niektóre odniesienia:
Nowe funkcje w Angular 2.3
Dziedziczenie komponentów w Angular 2
Plunkr demonstrujący dziedziczenie składników
źródło
More than one component matched on this element
Jeśli tego nie zrobisz, pojawi się błąd wykonania podobny do tego.@Component()
tagu. Możesz jednak udostępnić plik .html lub .css, odwołując się do tego samego pliku, jeśli chcesz. W sumie to duży plus.Teraz, gdy TypeScript 2.2 obsługuje mieszanki za pomocą wyrażeń klas , mamy znacznie lepszy sposób wyrażania miksów w składnikach. Pamiętaj, że możesz również użyć dziedziczenia komponentów od wersji angular 2.3 ( dyskusja ) lub niestandardowego dekoratora, jak omówiono w innych odpowiedziach tutaj. Uważam jednak, że Mixiny mają pewne właściwości, które sprawiają, że są preferowane do ponownego wykorzystania zachowania w różnych składnikach:
Zdecydowanie sugeruję przeczytanie powyższego ogłoszenia TypeScript 2.2, aby zrozumieć, jak działają Mixiny. Połączone dyskusje w kątowych problemach GitHub zawierają dodatkowe szczegóły.
Będziesz potrzebować tych typów:
Następnie możesz zadeklarować Mixin taki jak ten,
Destroyable
który pomaga komponentom śledzić subskrypcje, które należy usunąćngOnDestroy
:Aby wmieszać
Destroyable
w aComponent
, deklarujesz swój komponent w ten sposób:Pamiętaj, że
MixinRoot
jest to konieczne tylko wtedy, gdy chceszextend
stworzyć kompozycję Mixin. Możesz łatwo przedłużyć wiele miksów, npA extends B(C(D))
. To oczywista linearyzacja miksów, o której mówiłem powyżej, np. Efektywnie tworzysz hierarchię dziedziczeniaA -> B -> C -> D
.W innych przypadkach, np. Gdy chcesz skomponować składanki na istniejącej klasie, możesz zastosować składankę w następujący sposób:
Jednak okazało się, że pierwszy sposób działa najlepiej w przypadku
Components
iDirectives
, ponieważ te również muszą być ozdobione@Component
lub@Directive
tak czy inaczej.źródło
function Destroyable<T extends Constructor<{}>>(Base = class { } as T)
. W ten sposób mogę tworzyć miksy za pomocąextends Destroyable()
.aktualizacja
Dziedziczenie komponentów jest obsługiwane od wersji 2.3.0-rc.0
oryginalny
Jak dotąd, najbardziej wygodny dla mnie jest, aby zachować szablon i style w odrębny
*html
i*.css
plików oraz określenie tych, dziękitemplateUrl
istyleUrls
tak łatwo jest wielokrotnego użytku.źródło
@Input()
i@Output()
dekoratorów, prawda?O ile wiem, dziedziczenie komponentów nie zostało jeszcze zaimplementowane w Angular 2 i nie jestem pewien, czy mają takie plany, jednak skoro Angular 2 używa maszynopisu (jeśli zdecydowałeś się pójść tą drogą), możesz użyć dziedziczenia klas robiąc
class MyClass extends OtherClass { ... }
. Jeśli chodzi o dziedziczenie komponentów, sugerowałbym zaangażowanie się w projekt Angular 2, przechodząc do https://github.com/angular/angular/issues i wysyłając żądanie funkcji!źródło
export class MyPanelComponent extends BasePanelComponent
), problem występuje tylko w przypadku, gdy adnotacje / dekoratory nie są dziedziczone.@SubComponent()
), który oznacza klasę jako@Component
składnik podrzędny, lub dodatkowego pola w dekoratorze, które umożliwia odwołanie się do komponentu nadrzędnego w celu dziedziczenia.Pozwól nam zrozumieć niektóre kluczowe ograniczenia i funkcje systemu dziedziczenia komponentów Angulara.
Komponent dziedziczy tylko logikę klasy:
Należy pamiętać o tych cechach, więc przeanalizujmy każdą z nich niezależnie.
Komponent dziedziczy tylko logikę klasy
Kiedy dziedziczysz komponent, cała logika wewnątrz jest dziedziczona w równym stopniu. Warto zauważyć, że tylko członkowie publiczni są dziedziczeni, ponieważ członkowie prywatni są dostępni tylko w klasie, która je implementuje.
Wszystkie metadane w dekoratorze @Component nie są dziedziczone
Fakt, że żadne metadane nie są dziedziczone, może początkowo wydawać się sprzeczny z intuicją, ale jeśli się nad tym zastanowić, ma to sens. Jeśli dziedziczysz z elementu, powiedzmy (składnikA), nie chcesz, aby selektor składnika ComponentA, z którego dziedziczysz, zastępował selektor składnika ComponentB, który jest klasą dziedziczącą. To samo można powiedzieć o szablonie / templateUrl, jak również o style / styleUrls.
Właściwości komponentu @Input i @Output są dziedziczone
To kolejna funkcja, którą naprawdę uwielbiam w dziedziczeniu komponentów w Angular. W prostym zdaniu, gdy masz niestandardową właściwość @Input i @Output, te właściwości są dziedziczone.
Cykl życia komponentu nie jest dziedziczony
Ta część nie jest tak oczywista, zwłaszcza dla osób, które nie pracowały intensywnie z zasadami OOP. Na przykład załóżmy, że masz ComponentA, który implementuje jeden z wielu haków cyklu życia Angulara, takich jak OnInit. Jeśli utworzysz ComponentB i odziedziczysz ComponentA, cykl życia OnInit z ComponentA nie zostanie uruchomiony, dopóki nie zostanie jawnie wywołany, nawet jeśli masz ten cykl życia OnInit dla ComponentB.
Wywołanie metod komponentów Super / Base
Aby uzyskać metodę ngOnInit () z programu ComponentA fire, musimy użyć słowa kluczowego super, a następnie wywołać potrzebną metodę, którą w tym przypadku jest ngOnInit. Słowo kluczowe super odnosi się do instancji dziedziczonego komponentu, z którego w tym przypadku będzie ComponentA.
źródło
jeśli czytasz biblioteki CDK i biblioteki materiałów, używają one dziedziczenia, ale nie tak bardzo dla samych komponentów, projekcja zawartości jest królem IMO. zobacz ten link https://blog.angular-university.io/angular-ng-content/ gdzie jest napisane „kluczowy problem z tym projektem”
Wiem, że to nie odpowiada na twoje pytanie, ale naprawdę uważam, że należy unikać dziedziczenia / rozszerzania składników . Oto moje rozumowanie:
Jeśli klasa abstrakcyjna rozszerzona o dwa lub więcej komponentów zawiera logikę współdzieloną: użyj usługi lub nawet utwórz nową klasę maszynopisu, która może być współużytkowana między dwoma komponentami.
Jeśli klasa abstrakcyjna ... zawiera zmienne wspólne lub funkcje onClicketc, nastąpi duplikacja między kodem HTML dwóch rozszerzających się widoków komponentów. To jest zła praktyka i że współdzielony kod HTML musi zostać podzielony na komponenty. Te komponenty (części) mogą być współużytkowane przez te dwa komponenty.
Czy brakuje mi innych powodów, dla których warto mieć abstrakcyjną klasę komponentów?
Przykładem, który ostatnio widziałem, były komponenty rozszerzające funkcję AutoUnsubscribe:
był to bas, ponieważ w dużej bazie kodu
infiniteSubscriptions.push()
był używany tylko 10 razy. Również importowanie i rozszerzenieAutoUnsubscribe
faktycznie zajmuje więcej niż tylko dodanie kodumySubscription.unsubscribe()
wngOnDestroy()
metodzie samego komponentu, który wymagał dodatkowej logiki i tak.źródło
Jeśli ktoś szuka zaktualizowanego rozwiązania, odpowiedź Fernando jest prawie idealna. Z wyjątkiem tego, że
ComponentMetadata
zostało wycofane. Za pomocąComponent
zamiast tego zadziałało dla mnie.Pełny
CustomDecorator.ts
plik dekoratora niestandardowego wygląda następująco:Następnie zaimportuj go do nowego
sub-component.component.ts
pliku komponentu i użyj@CustomComponent
zamiast@Component
tego:źródło
Możesz dziedziczyć @Input, @Output, @ViewChild itp. Spójrz na przykład:
źródło
Komponenty można rozszerzać tak samo, jak dziedziczenie klas maszynopisu, tyle że trzeba nadpisać selektor nową nazwą. Wszystkie właściwości Input () i Output () z komponentu nadrzędnego działają normalnie
Aktualizacja
@Component jest dekoratorem,
Dekoratory są stosowane podczas deklaracji klasy, a nie na obiektach.
Zasadniczo dekoratory dodają pewne metadane do obiektu klasy i nie można uzyskać do nich dostępu przez dziedziczenie.
Jeśli chcesz uzyskać dziedziczenie dekoratora, zasugerowałbym napisanie niestandardowego dekoratora. Coś jak poniżej przykład.
Zobacz: https://medium.com/@ttemplier/angular2-decorators-and-class-inheritance-905921dbd1b7
źródło
źródło