Mam zestaw komponentów angular2, które powinny zostać wstrzyknięte. Moja pierwsza myśl była taka, że najlepiej byłoby stworzyć superklasę i wstrzyknąć tam usługę. Każdy z moich komponentów rozszerzyłby wtedy tę superklasę, ale to podejście nie działa.
Uproszczony przykład:
export class AbstractComponent {
constructor(private myservice: MyService) {
// Inject the service I need for all components
}
}
export MyComponent extends AbstractComponent {
constructor(private anotherService: AnotherService) {
super(); // This gives an error as super constructor needs an argument
}
}
Mógłbym to rozwiązać, wstrzykując MyService
każdy komponent i używając tego argumentu do super()
wywołania, ale to z pewnością jakiś absurd.
Jak poprawnie zorganizować moje komponenty, aby dziedziczyły usługę z superklasy?
new MyService()
zamiast wstrzykiwania daje dokładnie ten sam wynik (z wyjątkiem bardziej wydajnego). Jeśli chcesz udostępniać tę samą instancję usługi w różnych usługach i / lub składnikach, to nie zadziała. Każda klasa otrzyma innąMyService
instancję.myService
. Znaleziono rozwiązanie, które pozwala uniknąć tego, ale dodaje więcej kodu do klas pochodnych ...Odpowiedzi:
To nie jest absurd. Tak działają konstruktory i iniekcja konstruktora.
Każda klasa, którą można wstrzykiwać, musi zadeklarować zależności jako parametry konstruktora, a jeśli nadklasa również ma zależności, muszą one również zostać wymienione w konstruktorze podklasy i przesłane wraz z
super(dep1, dep2)
wywołaniem do nadklasy .Omijanie wtryskiwacza i uzyskiwanie zależności ma bezwzględnie poważne wady.
Ukrywa zależności, co utrudnia odczytanie kodu.
Narusza to oczekiwania osoby zaznajomionej z działaniem Angular2 DI.
Przerywa kompilację w trybie offline, która generuje kod statyczny w celu zastąpienia deklaratywnego i imperatywnego DI w celu poprawy wydajności i zmniejszenia rozmiaru kodu.
źródło
super
wywołania) do około 20+ klas i ta liczba będzie rosła w przyszłości. Tak więc dwie rzeczy: 1) nie chciałbym widzieć, jak robi to „duża baza kodu”; i 2) Dzięki Bogu za vimq
i vscodectrl+.
Zaktualizowane rozwiązanie zapobiega generowaniu wielu instancji myService przy użyciu globalnego wtryskiwacza.
Zapewni to, że MyService będzie można używać w dowolnej klasie, która rozszerza AbstractComponent bez konieczności wstrzykiwania MyService w każdej klasie pochodnej.
Jest kilka wad tego rozwiązania (zobacz komentarz @ Günter Zöchbauer pod moim oryginalnym pytaniem):
Bardzo dobrze napisane wyjaśnienie wstrzykiwania zależności w Angular2 znajduje się w tym poście na blogu, który bardzo pomógł mi rozwiązać problem: http://blog.hardtram.io/angular/2015/05/18/dependency-injection-in-angular- 2.html
źródło
this.myServiceA = injector.get(MyServiceA);
itd.?Injector
globalne, aby uniknąć konieczności łączenia jakichkolwiek parametrówAbstractComponent
? fwiw, myślę, że wstrzykiwanie zależności właściwości do szeroko stosowanej klasy bazowej w celu uniknięcia niechlujnego tworzenia łańcuchów konstruktorów jest całkowicie poprawnym wyjątkiem od zwykłej reguły.Zamiast ręcznie wstrzykiwać wszystkie usługi, utworzyłem klasę udostępniającą usługi, np. Pobiera ona wstrzyknięte usługi. Ta klasa jest następnie wstrzykiwana do klas pochodnych i przekazywana do klasy bazowej.
Klasy pochodnej:
Klasa podstawowa:
Klasa świadcząca usługi:
źródło
Zamiast wstrzykiwać usługę, która ma wszystkie inne usługi jako zależności, na przykład:
Pominąłbym ten dodatkowy krok i po prostu dodałbym wstrzyknięcie wszystkich usług w BaseComponent, na przykład:
Ta technika zakłada 2 rzeczy:
Twoje obawy są całkowicie związane z dziedziczeniem składników. Najprawdopodobniej powodem, dla którego trafiłeś na to pytanie, jest przytłaczająca ilość kodu nieosuszonego (WET?), Który musisz powtórzyć w każdej klasie pochodnej. Jeśli chcesz skorzystać z jednego punktu wejścia dla wszystkich swoich składników i usług , musisz wykonać dodatkowy krok.
Każdy składnik rozszerza
BaseComponent
Istnieje również wada, jeśli zdecydujesz się użyć konstruktora klasy pochodnej, ponieważ będziesz musiał wywołać
super()
i przekazać wszystkie zależności. Chociaż tak naprawdę nie widzę przypadku użycia, który wymaga użyciaconstructor
zamiastngOnInit
, jest całkowicie możliwe, że taki przypadek użycia istnieje.źródło
Z tego, co rozumiem, aby dziedziczyć z klasy bazowej, najpierw musisz ją utworzyć. Aby go utworzyć, musisz przekazać jego konstruktorowi wymagane parametry, więc przekazujesz je z dziecka do rodzica za pomocą wywołania super (), więc ma to sens. Innym dobrym rozwiązaniem jest oczywiście wtryskiwacz.
źródło
Jeśli klasa nadrzędna została pobrana z wtyczki innej firmy (i nie możesz zmienić źródła), możesz to zrobić:
lub w lepszy sposób (pozostań tylko jeden parametr w konstruktorze):
źródło
UGLY HACK
Jakiś czas temu niektórzy z moich klientów chcą dołączyć do dwóch BIG angular projektów do wczoraj (angular v4 w angular v8). Projekt v4 używa klasy BaseView dla każdego komponentu i zawiera
tr(key)
metodę tłumaczenia (w v8 używam ng-translate). Aby więc uniknąć przełączania systemu tłumaczeń i edytować setki szablonów (w v4) lub ustawić równolegle 2 system tłumaczeń, używam poniższego brzydkiego hacka (nie jestem z tego dumny) - wAppModule
klasie dodaję następujący konstruktor:a teraz
AbstractComponent
możesz użyćźródło