Angular - moduły, komponenty oraz dyrektywa ngModel

Stronę tą wyświetlono już: 32 razy

Wstęp

Tematem przewodnim tej podstrony będzie głównie opis tworzenia komponentów, tego czym one są oraz powiązanej z komponentami dyrektywy ngModel. Również poruszę temat tworzenia modułów oraz ich powiązania z komponentami.

Zanim jednak przejdę do sedna sprawy, wypadałoby zacząć od odpowiedzenia sobie pokrótce na pytanie: czym są komponenty? Otóż najkrótsza odpowiedź na to pytanie jest taka, że komponenty stanowią połączenie kodu klasy (który steruje działaniem wyświetlanego widoku), kodu widoku zapisanego w formacie HTML, styli z nimi powiązanymi CSS, SCSS czy też SASS (w zależności od opcji wybranych przez użytkownika).

Tworzenie modułu

Każdy komponent musi zostać podpięty w jakimś module, który z kolei jest podpięty do modułu głównego. Bez tego żaden komponent nie będzie działał. Z tego też względu w nowym folderze warto utworzyć sobie nowy moduł w następujący sposób:

ng g m sub

Oczywiście konieczne jest wykonanie tego polecenia z poziomu folderu projektu src/app. Tak utworzony moduł będzie wyglądał mniej więcej tak:

Listing 1
  1. import { NgModule } from '@angular/core';
  2. import { CommonModule } from '@angular/common';
  3. @NgModule({ // dyrektywa ngModel to dzięki niej dzieje się Angularowa magia
  4. declarations: [], // tutaj będą umieszczane deklaracje komponentów
  5. imports: [
  6. CommonModule // tutaj będą importowane moduły
  7. ]
  8. })
  9. export class SubModule { } // eksport modułu, tak aby można było go zaimportować w module głównym app.module.ts

Powyższy kod zapisany jest w utworzonym pliku sub.module.ts, wraz z którym utworzony został automatycznie nowy folder sub. I to do tego właśnie folderu będą wrzucane komponenty, serwisy, klasy i interfejsy, które są powiązane z tymże modułem.

Tworzenie pierwszego komponentu

W wcześniej wygenerowanym folderze sub można teraz oddać się niekończącej się rozkoszy utworzenia nowego komponentu o jakże dźwięcznej nazwie myFirst:

ng g c myFirst

Po wykonaniu powyższego polecenia z poziomu folderu utworzonego modułu sub w pliku sub-module.ts w deklaracjach pojawi się takie oto coś:

Listing 2
  1. declarations: [MyFirstComponent],

Jak widać utworzony komponent automatycznie został podpięty do pierwszego napotkanego nadrzędnego modułu.

Sam moduł komponent podzielony na pliki został utworzony w oddzielnym folderze o nazwie my-first. Są więc tutaj następujące pliki:

  • my-first.component.css - plik styli CSS;
  • my-first.component.html - plik kodu HTML;
  • my-first.component.spec.ts - plik z testami jednostkowymi komponentu;
  • my-first.component.ts - plik z kodem TypeScript-owym

My będziemy się skupiać głównie na plikach HTML i ts no i troszkę CSS. Zajrzyjmy więc łaskawym okiem do kodu pliku my-first.component.ts:

Listing 3
  1. import { Component, OnInit } from '@angular/core';
  2. @Component({
  3. selector: 'app-my-first', // selektor, którego będzie można użyć do osadzenia komponentu w kodzie HTML przykład: <app-my-first></app-my-first>
  4. templateUrl: './my-first.component.html', // ścieżka do pliku html powiązanego z kodem klasy komponentu
  5. styleUrls: ['./my-first.component.css' // ścieżka do pliku css z stylami powiązanymi z plikiem HTML komponentu
  6. })
  7. export class MyFirstComponent implements OnInit {
  8. constructor() { }
  9. ngOnInit() { // funkcja, którą Angular wywoła, gdy zmienne komponentu zostaną zainicjalizowane (uruchamiane raz krotko po utworzeniu komponentu
  10. }
  11. }

Ważne aby pamiętać, że zmienne publiczne klasy komponentu jak również i jej metody mogą być użyte w kodzie HTML, natomiast wszystkie zmienne czy funkcje nie wchodzące w skład klasy tegoż komponentu nie mogą być użyte w kodzie, gdyż ten jest przez Angular-a ściśle izolowany. To samo dotyczy się styli CSS, które mają zastosowanie i działanie tylko w obrębie danego komponentu.

Osadzanie komponentu w komponencie głównym

Pierwszy komponent został utworzony, aby można było zobaczyć rezultat jego działania należy go osadzić w kodzie HTML głównego komponentu, który znajduje się w pliku app.component.html. Osadzanie wygląda następująco:

Listing 4
  1. <app-my-first></app-my-first>

To jednak nie wystarczy, trzeba jeszcze dodać komponent MyFirstComponent do eksportu w pliku sub.module.ts w następujący sposób:

Listing 5
  1. import { NgModule } from '@angular/core';
  2. import { CommonModule } from '@angular/common';
  3. import { MyFirstComponent } from './my-first/my-first.component';
  4. @NgModule({
  5. declarations: [MyFirstComponent],
  6. imports: [
  7. CommonModule
  8. ],
  9. exports: [
  10. MyFirstComponentComponent
  11. ]
  12. })
  13. export class SubModuleModule { }

Zaś w pliku app.module.ts do deklaracji dodać MyFirstComponent

Ponieważ w standardowo wygenerowanym komponencie nie ma zbyt wiele ciekawego kodu, po uruchomieniu w konsoli polecenia:

ng serve

W przeglądarce powinien pokazać się standardowy kod następującej treści:

my-first works!

Osadzanie elementu kodu TypeScript w kodzie HTML

Angular umożliwia bardzo łatwe osadzanie kodu TypeScript w kodzie strony HTML co z kolei umożliwia dynamiczne renderowanie jej treści. Dla przykładu w klasie MyFirstComponent dopiszę sobie jedno pole:

Listing 6
  1. title: 'Tytuł strony';

by po chwili lub co najwyżej dwóch osadzić go w kodzie HTML w następujący sposób:

Listing 7
  1. <h1>{{ title }}</h1>

Oczywiście możliwe jest wykonywanie prostych operacji matematycznych, czy wykorzystanie metod stworzonych w klasie komponentu. Nie jest możliwe jednak wykorzystanie np. funkcji z biblioteki Math, gdyż te pochodzą spoza tejże klasy. Oto przykładowe dwa dodatkowe pola numeryczne dodane do klasy:

Listing 8
  1. nr1: number = 10;
  2. nr2: number = 20;

oraz proste działanie matematyczne osadzone w kodzie pliku HTML:

Listing 9
  1. <p>Działanie matematyczne: nr1 + nr2 = {{ nr1 + nr2 }}</p>

Oczywisty (przynajmniej mam taką nadzieję) wynik to:

Działanie matematyczne: nr1 + nr2 = 30

Bindowanie zmiennych z kontrolkami HTML

Kolejny podstawowy mechanizm umożliwiający łatwą komunikację z użytkownikiem stanowi bindowanie dwukierunkowe. Oto przykładowa kontrolka, która została zbindowana z zmienną klasy komponentu:

Listing 10
  1. <input type="number" [(ngModel)]="nr1"/><input type="number" [(ngModel)]="nr2"/>
  2. <p>Działanie matematyczne: nr1 + nr2 = {{ nr1 + nr2 }}</p>

Ponieważ wykorzystywane są tutaj elementy formularza konieczne jest w pliku sub.module.ts dodanie do importów FormsModule:

Listing 11
  1. import { FormsModule } from '@angular/forms'; // importowanie FormsModule
  2. @NgModule({
  3. declarations: [MyFirstComponent],
  4. imports: [
  5. CommonModule,
  6. FormsModule // dodany modół
  7. ],
  8. exports: [
  9. MyFirstComponentComponent
  10. ]
  11. })
  12. export class SubModule { }

W ten oto sposób do dwóch kontrolek HTML-a zostały podpięte zmienne sterujące a wszystko to za sprawą dwukierunkowego bindowania z wykorzystaniem dyrektywy ngModel. Bindowanie dwukierunkowe oznacza, że podpięta zmienna ustawia wartość kontrolki i na odwrót, kontrolka ustawia wartość zmiennej. Poniższy wynik został uzyskany za pomocą czystego JavaScript-u w celu zasymulowania działania kodu Angulara.

Działanie matematyczne: nr1 + nr2 =

Dla porównania kod JavaScript wymieszany z HTML-em wygląda tak:

Listing 12
  1. <div><input id="nr1" type="number" value="10"><input id="nr2" type="number" value="20"></div>
  2. <div>Działanie matematyczne: nr1 + nr2 = <span id="sum"></span> <script>function add() { document.querySelector('#sum').innerText = (parseInt(document.querySelector('#nr1').value) + parseInt(document.querySelector('#nr2').value)).toString();
  3. }
  4. add();
  5. document.querySelector('#nr1').addEventListener('change', add);
  6. document.querySelector('#nr2').addEventListener('change', add);
  7. </script>

Komentarze