Poprzednie wpisy wprowadzały nas coraz głębiej w świat Angulara. Nie robiliśmy niczego skomplikowanego:
poznaliśmy wymagane narzędzia;
zapoznaliśmy się nieco z CLI;
utworzyliśmy pierwszą aplikację;
zmodyfikowaliśmy strukturę projektu;
dodaliśmy nowy komponent;
mieliśmy styczność z podstawowymi dyrektywami.
Przed dalszą częścią wyprawy warto nieco głębiej zgłębić temat archituktury Angulara.
Angular 8 jest platformą i frameworkiem, który pozwala na budowanie aplikacji klienckich przy wykorzystaniu HTML oraz TypeScript. Sam również został napisany w TypeScript. Implementacja rdzenia (oraz dodatkowych funkcjonalności) odbywa się za pomocą zestawu bibliotek TypeScript, które można importować do tworzonej aplikacji.
Bardzo istotnymi (a jednocześnie podstawowymi) elementami składowymi aplikacji Angular są NgModules - zapewniają odpowiedni kontekst dla używanych komponentów. NgModules gromadzą powiązany kod w ramach zdefiniowanych zestawów funkcjonalności. Aplikacja zawsze posiada przynajmniej jeden moduł główny, który pozwala na ładowanie innych modułów posiadających określoną implementację. Jeżeli macie jakiekolowiek wątpliwości zapraszam do poprzedniego wpisu:
Angular 8: komponenty
Wspomniane wcześniej komponenty definiują widoki, które, dzięki napisanej logice, mogą być modyfikowane zgodnie z implementacją i danymi aplikacji. Sama logika pozwala na wstrzykiwanie różnych zależności dzięki czemu napisany kod staje się kodem modułowym oraz wielokrotnego użytku.
Zwróćcie uwagę na kluczowe elementy architektury Angulara:
Moduły
Aplikacja Angular 8 składa się z modułu głównego zwanego
AppModule. Zapewnia on mechanim początkowego ładowania, które pozwala na uruchomienie aplikacji. Szczegóły proces od momentu wywołania polecenia ng serve do uruchomienia aplikacji został omówiony we wpisie: Angular 8: ładowanie aplikacji
W pełni funkcjonalna aplikacja składa się z wielu modułów implementujących określone (inne) funkcjonalności.
Do najważniejszych cech modułów możemy zaliczyć:
importowanie dodatkowych funkcjonalności z innych NgModules;
eksport własnych funkcji i wykorzysywanie ich przez inne NgModules. Prostym przykładem może być wykorzystanie routera w aplikacji – możemy dokonać importu modułu Router (temat ten zostanie omówiony w osobnym wpisie ponieważ jest niezywkle istotny);
Komponenty
Na ich temat wiemy już nieco więcej – głównie ze względu na ich znaczenie w aplikacji i eksperymenty, które prowadziliśmy. Nie wykorzystaliśmy jednak jeszcze ich klas (w świadomy sposób) ponieważ poruszaliśmy jedynie zagadanienia podstawowe. Ten temat rozszerzymy we wpisie dotyczącym ‘wiązania właściwości’. Warto jednak mieć w pamięci, że każdy komponent definiuje klasę zawierającą dane aplikacji oraz logike i jest powiązany z szablonem HTML, który definiuje widok wyświetlany jako część aplikacji.
Zanim pójdziemy dalej musimy poświęcić parę chwil na znaczenie metadanych. Z perspektywy mojego doświadczenia z frameworkiemOracle JET wiem, że są niezwykle istotne (dodatkowo niektóre z ich komponentów z czasem stają się przestarzałe więc trzeba w miarę na bieżąco śledzić postępy prac związane z nadchodzącymi wersjami). Metadane klasy komponentu są kojarzone/powiązane z szablonem definiującym widok. Szablon łączy zwykły kod HTML z dyrektywami Angulara oraz znacznikami wiązania (temat data-binding omówimy później), które pozwalają Angularowi modyfikować HTML przed renderowaniem danego widoku i przygotowaniem go do wyświetlenia. W później części pracy z tą plalformą wykorzystamy wpis: .NET Core: tworzenie danych na potrzeby fake API w celu przygotowania danych przy użyciu .NET Core Web API.
Proces dostarczania danych do komponentów odbywa się przez wstrzykiwanie zależności (Dependency Injection).
Szablony, dyrektywy i wiązanie danych
Szablony w Angular 8 służą do łączenia kodu HTML ze znacznikami wiązania w celu modyfikacji kodu HTML przed wyświetleniem. Z kolei specjalne dyrektywy pozwalają na implementacje odpowiedniej logiki.
Możemy wyróżnić dwa typy wiązania danych:
Event Binding: wiązanie zdarzeń służy do wiązania zdarzeń z aplikacją i reagowania na dane wejściowe użytkownika poprzez aktualizację danych wyświetlanych w aplikacji;
Property Binding: polega na przekazywaniu danych z klasy komponentu zapewniając łączenie danych aplikacji z drzewem DOM.
Usługi i wstrzykiwanie zależności
Angular 8 pozwala na tworzenie klasy usługi dla danych lub logiki biznesowej, która nie jest powiązana z konkretnym widokiem. Klasa taka może zostać udostępniona pomiędzy komponentami.
Tutaj z pomocą przychodzi nam Dependency Injection. Mechanizm ten nie pobiera danych z serwera, nie przeprowadza walidacji danych wejściowych oraz nie odpowiada za logowanie żadnych informacji do konsoli – takie zadania przekazywane są wyżej wspomnianym usługą. Wstrzykiwanie zależności pozwala na tworzenie lekkich i wydajnych usług.
Aby nieco rozjaśnić powyższe wyjaśnienie spojrzymy na prosty przykład. Wyobraźmy sobie usługę, której zadaniem jest wypisywanie różnych poziomów logów w oknie konsoli przeglądarki. Poniżej przykładowa implementacja:
Usługi mogą oczywiście zależeć od innych usług. Wyobraźmy sobie serwis, który zależy od serwisu Logger ale również od drugiego serwisu, którego celem jest pobieranych danych (BackendService). Ten serwis z kolei będzie zależał od zaimplementowanego serwisu HttpClient, który pozwala na asynchroniczne pobieranie danych z serwera:
Dependency Injection jest używany w aplikacjach Angular dosłownie wszędzie – pozwala dostarczać komponentom wymagane usługi. Usługi takie są wstrzykiwane do komponentu umożliwiając dostęp do jego klasy (a zarazem całej funkcjonalności).
Routing
W Angular 8Router jest modułem NgModule, który zapewnia usługę pozwalającą programistom na definiowanie ścieżek nawigacji pomiędzy różnymi stanami oraz hierachią widoków aplikacji. Czemu wspominam o różnych stanach? Wyobraźcie sobie np. widok konfiguracji sytemu który posiada wiele zakładek, tj. ustawienia główne, status serwera, konfiguracja serwisów, etc. Router pozwala na nadanie stanu każdej z zakładek – dzięki temu dysponując adresem URL jesteśmy w stanie ponownie otworzyć aplikację na danej stronie oraz zaznaczyć wskazaną zakładkę.
Generalnie zasada działania jest taka sama jak działa nawigacja przeglądarki:
wprowadzenie adresu URL prowadzi do odpowiedniej strony w aplikacji;
kliknięcie linka na stronie spowoduje przejście przeglądarki pod wskazany adres (tj. widok);
klikając wstecz przy użyciu przycisków przeglądarki aplikacja będzie w stanie przejść do odpowiedniej strony ‘do tyłu’ zgodnie z historią przeglądania.