Heja! Angular Tips & Tricks będzie nową serią artykułów, w których będę pokazywał drobne rzeczy, porady, dobre praktyki, tricki, które będą dość krótkie, więc same w sobie nie zasługują na osobne artykuły. No to zaczynamy ; )
1. Struktura SharedModule
Zapewne stosowanie SharedModule (moduł dla komponentów współdzielonych w całej aplikacji, które mają wiele instancji, np. modale, taby etc.) i CoreModule (moduł dla komponentów, które mają jedną instancję dla całej aplikacji, np. login / sidebar) jest Ci znane. Jak poprawnie trzymać nasze komponenty w SharedModule? Jeśli trzymasz je jak poniżej:

To możesz to zrobić lepiej:

Różnica jest oczywista. Nie duplikujemy nazw komponentów, moduł jest czytelniejszy i łatwiejszy w utrzymaniu, bo tylko raz deklarujemy nazwę. Już przy około 45 komponentach, sharedModule w wariancie II ma około 40% mniej objętości kodu niż w wersji 1.
2. Rozbudowana dyrektywa ngClass
Dyrektywa ngClass jest bardzo przydatna. O ile opiera się na 1 klasie, np:
<div [ngClass]="{'tab-active': isTabActive}">Settings</div>
To jest jak najbardziej w porządku. Ale jak dochodzimy do bardziej rozbudowanych wariantów, np.
<div [ngClass]="{'active-tab': isTabActive && isTabInView, 'submenu': isSubMenu, 'multi-tab': isMultiTab }">Settings</div>
To wiedz, że dzieje się źle! Zaśmiecamy templatkę i wynosimy zbyt dużo logiki do widoku. Lepiej zrobić to w następujący sposób:
- w klasie komponentu tworzymy gettera:
get getCssClassesForTab() : void {
return {
'active-tab': this.isTabActive && this.isTabInView,
'submenu': this.isSubMenu,
'multi-tab': this.isMultiTab
};
}
- i na widoku wyłącznie:
<div [ngClass]="getCssClassesForTab">Settings</div>
Podobny patent polecam dla rozbudowanych dyrektyw ngStyle.
3. Kolejność deklaracji dyrektyw ma znaczenie
Wyobraźmy sobie przykład, że jedna dyrektywa korzysta z atrybutów, nadanych przez inną dyrektywę na tym samym elemencie, np:
<p directiveOne directiveTwo>Hello World</p>
DirectiveTwo nadaje elementowi atrybut X, a dyrektywa DirectiveOne korzysta między innymi z atrybutu X do zrobienia czegoś tam innego. DirectiveOne będzie mieć dostęp do atrybutu X, pod warunkiem, że directiveTwo została wcześniej zadeklarowana w module w tablicy declarations, tzn:
declarations: [ DirectiveTwo, DirectiveOne ];
Kolejność ich wypisania w tagu HTML nie ma znaczenia! Jeśli dyrektywy są zadeklarowane w osobnych modułach (DirectiveOne w ModuleOne, a DirectiveTwo w ModuleTwo), to znaczenie ma kolejność importów modułów:
imports: [ModuleTwo, ModuleOne].
Dobrze to wiedzieć, ponieważ Angular nie zwróci Ci błędu. Twoja dyrektywa po prostu otrzyma undefined na danym atrybucie / wartości pobranym z innej dyrektywy.
4. Atrybut Style i jednostki
Jeśli używasz dyrektywy style wraz z procentami w ten sposób:
<div [style.width]="someValue + '%'">Hey!</div>
To lepszym rozwiązaniem jest przeniesienie procentów do dyrektywy, tzn:
<div [style.width.%]="someValue">Hey!</div>
Podobnie możesz robić ze wszystkimi jednostkami (np. em, px, rem etc).
5. Wyświetlenie komponentu-dziecka, dopiero jak otrzyma dane
W aplikacjach opartych o komponenty sporo korzystamy z zagnieżdżonych komponentów. Jeśli komponent rodzic przekazuje do komponentu dziecka dane asynchroniczne, to musimy w jakiś sposób powiedzieć Angularowi, aby dopiero załadował komponent dziecko, jak dane dostaną dostarczone (inaczej otrzymamy błąd). Możemy to zrobić w bardzo prosty sposób, z użyciem *ngIf, np:
<user-list [users]="users" *ngIf="users"></user-list>
Dzięki prostemu trickowi *ngIf, nasz komponent user-list poczeka z ukazaniem się na widoku, aż komponent-rodzic dostarczy mu listę userów.
6. Rekurencja na widoku
Potrzebujesz wyświetlić dane z pliku JSON, a nie wiesz ile będzie poziomów zagnieżdżenia tego samego typu obiektu? Nic nie stoi na przeszkodzie, aby w templatce komponentu, odnosić się do selektora tworzonego komponentu, tak samo jak w środku funkcji, możesz wołać nazwę tej funkcji w celu uzyskania rekurencji.
Przykładowo masz pliki JSON z drzewami genealogicznymi, każde dziecko ma swoje dzieci, ale dostajemy rodziny z różną ilością pokoleń. Możemy załatwić to następująco:
@Component({
selector: 'children',
template: `
<div *ngFor="let child of children">
<ul>
<li>
{{child.name}}
<children [children]="child.children" *ngIf="child.children"></children>
</li>
</ul>
</div>
`,
})
export class ChildrenComponent {
@Input() children;
}
Czyli w templatce komponentu Children wołamy komponent Children.
Plunker do powyższego przykładu:
7. Router navigate i promise
Między innymi w oficjalnym tutorialu Angulara spotykamy się z metodą navigate() klasy Router, która może wyglądać następująco:
gotoDetail() {
this.router.navigate(['/detail', this.selectedHero.id]);
}
Co ciekawe, metoda navigate zwraca nam Promise<boolean>, dzięki temu po zakończeniu nawigacji, możemy wywołać inną funkcję, umieszczoną w .then(), (np. pokazać tosta):
gotoDetail() {
this.router.navigate(['/detail', this.selectedHero.id]).then((data) => {
console.log(data, 'promise resolved')
});
}
Argument data w powyższej funkcji, przyjmuje wartość true (promise resolved) lub false (promise rejected).
To by było na tyle dzisiaj! Co jakiś czas będę wrzucać zebrane złote myśli 🙂
