Ten artykuł jest częścią serii Tworzenie aplikacji webowej: Krok po kroku (15 / 19)

W poprzednim artykule omówiliśmy podstawową architekturę aplikacji angularowej. Dziś stworzymy nasz pierwszy komponent.

Wymagania wstępne:

Spisane w formie checklisty (do wydrukowania bądź podglądu/importu jako szablon Nozbe):

Checklista do wydrukowania

Szablon Nozbe do podglądu i pobrania

Materiały do nauki oraz informacje co i jak znajdziesz we wpisie Tworzymy aplikację  webową – krok po kroku – podsumowanie aktualnego statusu & co dalej?

 

Branch, odpowiadający dzisiejszemu artykułowi: AngularCLIApp (I część artykułu) oraz 02-first-component (II część artykułu)

 

Struktura projektu

K’woli przypomnienia, mamy katalog app, a w nim kilka plików:

angular src folder

Na nasz komponent składają się:

  • component.html
  • component.scss
  • component.spec.ts
  • component.ts

Tak jak mogą sugerować rozszerzenia:

  • *.html jest plikiem opisującym strukturę wizualną naszego komponentu.
  • *.scss nadaje mu styl (.css, jeśli wygenerowano projekt bez opcji –style=scss).
  • *.ts logika komponentu aplikacji.
  • A *.spec.ts? Plik testów, który tymczasem pominiemy.

 

Template (view)

Czyli właśnie struktura aplikacji przechowywana w pliku *.html.

Jest to standardowy HTML z wykorzystaniem pewnych „rozszerzeń” (*ngFor, (click), [item], itp.), tzw. Angular’s template syntax (zajmiemy się nim bardziej szczegółowo w oddzielnym artykule, już poza serią).

Widok aplikacji angularowej składa się właśnie z takiego rozbitego na wiele plików HTML’a. Zamiast umieścić wszystko w jednym pliku HTML, każdy komponent ma swój widok.

 

Component (controller/viewmodel)

Template odpowiada za widok. Logikę aplikacji przechowują klasy komponentów. Dla odróżnienia ich roli stosujemy rozszerzenie .component.ts. Tak samo stosujemy rozszerzenia .service.ts dla serwisów, .model.ts dla modelów, itd.

Ale komponenty rozróżniamy nie tylko po rozszerzeniu pliku. Mam nadzieję, że łatwo odgadnąć co wskazuje na to, że poniższa klasa jest komponentem?

Tak, kolejny dekorator – @Component. A w nim:

  • selector – który wskazuje tag odpowiadający danemu komponentowi (innymi słowy, w tym przypadku <app-root> wstawione w index.html, powoduje umieszczenie tam AppComponent),
  • templateUrl – adres template’a dla danego komponentu,
  • styleUrls – adres stylów dla danego komponentu, przy czym może to być więcej niż jeden plik.

Tłumacząc jeszcze prościej: templateUrl oraz styleUrls opisują strukturę i wygląd komponentu, dla którego logika przechowywana jest w klasie AppComponent (powyżej). Selector wskazuje miejsce wykorzystania tego komponentu. Przy czym oczywiście tych miejsc może być więcej niż jedno 😉

 

Data binding

Mamy widok. I mamy logikę. Jak je teraz połączyć? Jak komunikować się pomiędzy template’m a logiką komponentu?

Wykorzystując data binding.

Wyróżniamy cztery formy tzw. data binding syntax:

  • interpolacja: {{ value }}
  • property binding: [property] = „value”
  • event binding: (event) = „handler”
  • two-way data binding: [(ng-model)] = „property”

 

Czym się różnią?

Kierunkiem komunikacji:

Component -> DOM :

Interpolacja

{{ value }} – wyłuskanie własności komponentu do template’a, np.

.ts:

.html

 

Property binding

 

DOM -> Component

event binding

 

Komunikacja w obie strony

Two-way data binding

Jak widać połączenie property oraz event binding.

 

Powiem tak: jeśli miałabym teraz opisywać to wszystko bardziej szczegółowo – tutorial będzie się ciągnął jeszcze dwa lata. Także jeśli coś jest dla Ciebie niezrozumiałe, zapraszam do zapoznania się z miejscami, gdzie możesz uzyskać więcej wiedzy w opisywanym temacie: Angular Architecture Overview (Components, Templates, MetaData, Data binding)

Jeśli jednak masz jakieś pytania odnośnie przedstawionej treści – dawaj znać w komentarzu. Postaram się pomóc.

 

Let’s code!

Spróbujmy teraz zastosować tą wiedzę w praktyce.

Wyobraźmy sobie naszą aplikację:

tutorial app screen

Tak naprawdę potrzebujemy czterech komponentów:

  • komponentu całej aplikacji
  • komponentu dla formularza dodawania nowych zadań na listę
  • komponentu listy
  • komponentu zadania

 

Od którego z nich zaczniemy?

Prywatnie zaczynam zazwyczaj od ogółu do szczegółu. Ale tak naprawdę możesz zacząć od dowolnego komponentu.

Komponent całej aplikacji już mamy, wystarczy go zmodyfikować.

Z zawartości pliku app.component.html pozostawmy fragment:

Widzimy, że została tu zastosowana interpolacja. Skąd bierz się title? Z pliku app.component.ts:

Zmieńmy go na „Todo Sample App”:

 

Zwróć uwagę, że jeśli uruchomisz aplikację z ng serve, za każdym zapisem pliku widok będzie się odświeżał. Jest to bardzo wygodne 🙂

 

Jak mogłaby wyglądać struktura tej aplikacji?

Oczywiście jest to struktura bardzo statyczna 😛 Nie będziemy dodawać teraz w kodzie każdego zadania na naszą listę To do. Trzeba oprogramować całą logikę aplikacji. Ale poniekąd mówi nam o tym jak będą wyglądać nasze komponenty 😉

W artykule nt. Angular CLI wspominałam, że z czasem omówimy również tworzenie komponentów z wykorzystaniem Angular CLI. Ten dzień nadszedł.

 

Angular CLI: Generowanie nowych komponentów

Stwórzmy nowy komponent. Komponent listy zadań. Nazwiemy go todoList.

ng g c todoList –style=scss

  • g – skrót od generate
  • c – skrót od component
  • todoList – nazwa komponentu (+ ewentualnie ścieżka do niego, relatywnie w stosunku do miejsca wywoływania polecenia)
  • –style=scss – .scss zamiast standardowego .css

W odpowiedzi wygenerujemy folder dla danego komponentu, z dedykowanymi plikami. Zawartość tego folderu będziemy rozszerzać 🙂

 

Teraz tak: nie wiem dlaczego w przypadku Visual Studio Code muszę używać zapisku –style=scss w przypadku każdego pojedynczego komponentu. W przypadku WebStorm’a wystarczy ustawić style scss dla całego projektu. Każdy kolejny komponent będzie generowany z *.scss. Niestety w Visual Studio Code – tak to nie działa. Jeśli znasz obejście tego problemu – daj znać w komentarzu 😉

Więcej na temat możliwych opcji generowania z Angular CLI

 

Jeśli chcemy umieścić folder danego komponentu w folderze innego – nazwę komponentu poprzedza ścieżka (relatywna w stosunku do app/src).

E:\TodoWebAppSeries\nettecode_twa_sample\todoSampleWebApp>ng g c todoList

Znajdujemy się na tzw. root’cie aplikacji. Folder app-list wyląduje w src/app.

E:\TodoWebAppSeries\nettecode_twa_sample\todoSampleWebApp>ng g c todoList/todo

Znajdujemy się w tym samym miejscu. Folder appTodo wyląduje w folderze src/app/ todoList.

E:\TodoWebAppSeries\nettecode_twa_sample\todoSampleWebApp\src\app\app-list>ng g c appDone

Znajdujemy się w folderze app-list. Nowy folder appDone wyląduje jak powyżej, w app-list.

 

Zastanawiałam się czy w ogóle jest sens to opisywać. Dla większości z Was pewnie to banalne, ale wolałam zwrócić na to uwagę choćby po to by nie bać się zagnieżdżania folderów wewnątrz innych folderów. Początkowo jest odruch by każdy komponent lądował na root’cie (w src/app), aplikacja zaczyna się szybko powiększać, niekoniecznie ukazując pewne zależności.

Zresztą są różne szkoły jeśli chodzi o strukturyzację aplikacji. I będę jeszcze o tym pisać. Ale jeśli ktoś jest zainteresowany – przykład polecanej struktury.

 

Wiesz już jak tworzyć nowe komponenty z wykorzystaniem Angular CLI. Utwórz komponent todoList (jeśli jeszcze go nie masz):

ng g c todoList –style=scss

Przenieśmy narazie jedną z list, listę zadań do zrobienia.

Co powinno być customizowalne?

Na pewno nazwa listy.

W todo-list.component.html wykorzystajmy interpolację do pozyskania nazwy

z pliku todo-list.component.ts:

Gotowe?

Nie wiem czy do końca potrzebujemy listy, która ma przypisaną nazwę. Kto powinien wybierać nazwę listy? Komponent, który ją realizuje czy ten, który o nią prosi? Oczywiście druga odpowiedź.

Jest to komponent typowo wizualny. Nie ma myśleć o tym, co wyświetla. Ma wyświetlić to co chcemy. To, co mu przekażemy. Cała lista „to do” musi przyjść z zewnątrz.

Jak więc tego dokonać?

Tak jak już wspominałam, żeby umieścić komponent w danym miejscu struktury rodzica, dodajemy go w template’cie rodzica:

Możemy mu przekazać parametry korzystając z data binding.

Jakiego data binding tu potrzebujemy? Jeśli chodzi o nazwę wystarczy one-way data binding, czyli możemy skorzystać z property binding.

No ale przydałoby się gdyby nasz AppComponent, który wywołuje TodoList component miał co przekazać.

Nie obędzie się również bez współpracy z drugiej strony. TodoList component musi zadeklarować, że oczekuje na dane. Korzystamy w tym celu z dekoratora @Input:

Który najpierw musimy pozyskać dopisując go do importu z @angular/core:

Ok. Gotowe. Przekazaliśmy nazwę listy do komponentu.

Efekt?

app screen list

Skromnie i powoli, ale do przodu.

 

Mamy listę. Lista ma nazwę. Czas dodać do niej pierwsze zadanie…

Ale to już będzie temat kolejnego artykułu. Myślę, że jest sporo wiedzy do przyswojenia, jeśli to Twoja pierwsza aplikacja. Za tydzień zajmiemy się komponentem elementu listy. Naszym „to do”.

 

Przypominam, że kod aplikacji po dzisiejszej lekcji znajdziesz na Github’ie, branch: 02-first-component

Jeśli chcesz podejrzeć same zmiany: https://github.com/nettecode/todoSampleWebApp/commit/3cfcb9dc705c5f44ecf0670a662743d0608931e4

 

W razie pytań – śmiało dawaj znać w komentarzu lub na naszej Tutorialowej Grupie Wsparcia na Facebooku.

Powodzenia!

Series Navigation<< Let’s code!Komponent pojedynczego zadania & dyrektywa strukturalna *ngFor >>