Komponenty

Komponent kontroluje fragment ekranu zwany widokiem. Składa się z trzech podstawowych elementów - logiki, szablonu oraz styli.


* - css / less / scss

Komponenty, obok modułów, są podstawowym elementem budującym aplikację.

Składowe komponentu

Component to klasa udekorowana funkcją @Component. Przyjmuje ona metadane opisujące w jaki sposób należy zinterpretować i wyświetlić komponent oraz czego potrzebuje on do działania.

@Component({
  selector: 'app-loading',
  styleUrls: ['./loading.component.css'],
  template: `<md-spinner></md-spinner>`,
  providers: []
})
export class LoadingComponent {}

selector

Selektor definiuje element HTML, który zostanie wypełniony przez zawartość komponentu. W przypadku komponentów, które są wykorzystywane w routingu parametr selector może zostać pominięty.

template / templateUrl

Szablon komponentu można zdefiniować na dwa sposoby. Liniowo - umieszczając go w tym jednym pliku z logiką - wykorzystując parametr template. Drugą możliwością jest umieszczenie szablonu w oddzielnym pliku - służy do tego parametr templateUrl.

styles / stylesUrl

Podobnie jak szablon tak i style możemy zdefiniować zarówno liniowo jak i w osobnym pliku. Różnica polega na możliwości użycia wielu plików ze stylami. Style komponentu są domyślnie ograniczone tylko do jego elementów.

Definiując style komponentu mamy do dyspozycji dwa specjalne pseudoelementy - :host oraz :host-context().

:host pozwala się odwołać do elementu okalającego zawartość komponentu - nazywa się on tak jak selektor, o ile został zdefiniowany.

:host-context(.class-name) pozwala odwołać się do kontekstu elementu okalającego zawartość komponentu i sprawdzić czy na przykład na on nadaną klasę class-name.

providers

Tablica providers definiuje serwisy, które zostaną dostarczone do komponentu podczas jego tworzenia.

- - -

Przyjrzyjmy się komponentowi BeerDetailsComponent v4 (github).

Dzielenie na komponenty

Kiedy nasza aplikacja zaczyna się rozrastać pojawiają się miejsca gdzie powtarzają się elementy interfejsu. Warto w takich wypadkach uniknąć redundancji kodu i stworzyć reużywalny komponent. Warto aby były one tworzone wedle zasady SRP (ang. single responsibility principle).

W naszym przypadku takim komponentem będzie karta z detalami pojedynczego piwa (3).

Wydzielimy nasz komponent i wykorzystamy go na widokach detali piwa oraz nowym pokazującym losowe piwo.

Aplikacja po wydzieleniu i wykorzystaniu BeerCardComponent v6 (github).

Aby zachować spójność aplikacji wydzielone zostały również komponenty zawierajace nagłówek strony (1) oraz listę piw. Aplikacja po podzieleniu na komponenty v7 (github).

Interakcje między komponentami

Podział widoków na komponenty i umieszczanie ich w sobie sprawia iż konieczna staje się komunikacja pomiędzy nimi.

input

Pole komponentu udekorowane funkcją @Input() pozwala na przekazanie do niego wartości przez rodzica przy użyciu kwadratowych nawiasów [ ].

output

Pole komponentu udekorowane funkcją @Output() pozwala na przekazanie wartości do rodzica przy użyciu okrągłych nawiasów ( ). Pole to musi być typu EventEmitter<T>.

- - -

Aby zademonstrować komunikację za pomocą input-ów oraz output-ów dodamy możliwość filtrowania po frazie listy piw na żywo.

Aplikacja po stworzeniu i wykorzystaniu BeersSearchComponent v8 (github).

custom two-way binding

Łącząc ze sobą parę input - output możemy stworzyć własny two-way binding - działający tak jak [(ngModel)]. Ważną kwestią są w tym przypadku nazwy pól. Pole output musi mieć nazwę składającą się z nazwy pola input oraz sufiksu Change.

@Input() value: string;
@Output() valueChange: EventEmitter<string> = new EventEmitter<string>();

- - -

W łatwy sposób możemy wykorzystać two-way binding w BeersSearchComponent. Aplikacja po zmianach v9 (github).

@ViewChild

Dekorator @ViewChild pozwala uzyskać uchwyt do komponentu dziecka. Dzięki temu możemy wywołać jego publiczne metody oraz zyskujemy dostęp do publicznych pól.

@ViewChild(BeerCardComponent) private beerCard: BeerCardComponent;

- - -

Obecnie wyszukiwanie piw jest aktywne od razu po wyświetleniu widoku nawet kiedy jeszcze nie załadowała się lista piw. Dobrym rozwiązaniem będzie uaktywnianie pola wyszukiwania po załadowaniu danych.

Aplikacja po zmianach v10 (github).

ng-content

Podczas tworzenia komponentów zdarza się, że chcemy stworzyć komponent, który stanowi strukturę dla innych i będzie można je wypełnić dowolną treścią. Z pomocą przychodzi nam element <ng-content>, który pozwala na umieszczenie w szablonie komponentu zawartości umieszczonej wewnątrz jego tagów (ustalonych przez selektor).

- - -

Dobrym przykładem jest popup. Stanowi on jedynie formatkę, którą następnie, wedle potrzeb, możemy dowolnie wypełnić.

W aplikacji pojawiła się funkcjonalność ulubionych piw v15 (github). Obecnie usuwanie piw z ulubionych odbywa się bez żadnego potwierdzenia. Łatwo można przypadkowo usunąć ulubione piwo. Dodamy popup z prośbą o potwierdzenie chęci usunięcia ulubionego piwa.

Aplikacja po zmianach v16 (github).

Dwa rodzaje komponentów

Chociaż podział ten nie istnieje w samym frameworku, w toku prac nad aplikacjami wyróżniłem dwa rodzaje komponentów - widoki oraz reużywalne komponenty.

Reużywalne komponenty skupiają się na zdefiniowaniu pojedynczego elementu interfjesu - wedle zasady SRP. Przyjmują dane, zajmują się ich obróbką a następnie je wyświetlają.

Widoki są komponentami, które są używane w routingu. Definiują on strukturę widoku przy wykorzystaniu reużywalnych komponentów. Zajmują się również pobieraniem danych potrzebnych do wyświetlenia. Obsługują też komunikację z serwerem podczas obsługi akcji użytkownika.


Źródła

results matching ""

    No results matching ""