Witam,
dziś zajmiemy się podziałem aplikacji na komponenty. Cały nasz komponent główny czyli app.component.html napiszemy od nowa i podzielimy go na kilka mniejszych swego rodzaju klocków. Będzie to wprowadzenie do angularowych komponentów. W dalszych wpisach nauczymy się komunikować poszczególne komponenty ze sobą. Pokażę jak ułatwić sobie zadanie z Angular CLI w tworzeniu nowych komponentów. Stworzymy także osobny moduł by podzielić naszą aplikacje na logiczne moduły.
Zaczynamy
W dzisiejszym wpisie chciałbym wprowadzić pewne udogodnienie. Mianowicie System Kontroli Wersji Git. Dla niewiedzących odsyłam do znakomitego poradnika do którego linki dałem w notatkach.
Wersja wyjściowa aplikacji znajduje się pod adresem: https://github.com/taaaniel/foodCalc/tree/part_5_component_modules_start
Czym różni się moduł od komponentu? Moduł jest jakby zbiorem komponentów, dyrektyw, filtrów itp. Elementy te muszą być w tym module powiązane ze sobą, czyli w skrócie moduł grupuje powiązane ze sobą komponenty, dyrektywy lub inne moduły. W naszej aplikacji, dla przykładu, będzie jeden główny moduł który będzie grupował pozostałe moduły. Jeden moduł tzw „shared module” w którym będą powtarzające się inne moduły, komponenty czy filtry np moduły i komponenty z Angular Material. I moduły poszczególnych funkcjonalności aplikacji (Dashboard, Własne potrawy itp)
- Importuje BrowserModule, pozwala on nam wykorzystywać dyrektywy jak na przykład ngIf, nfFor .
- Posiada właściwość bootstrap, czyli listę elementów, które są punktami startowymi aplikacji. U nas jest to jeden element. Główny komponent aplikacji.
- Konwencja mówi, że moduł główny powinien nazywać się AppModule
Stwórzmy najpierw nasz „shared module” i zaimportujmy tam moduły z Material Design. Będąc w katalogu aplikacji w konsoli i używając komendy:
ng generate module shared
Angular CLI stworzy nam katalog „shared” w katalogu app a także wygeneruje moduł shared.module.ts który na początku wygląda tak:
import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; @NgModule({ declarations: [], imports: [ CommonModule ] }) export class SharedModule { }
Teraz z moduł app.module.ts kopiujemy wszystkie zaimportowane moduły z Angular Material i FlexBox a następnie wpisujemy do naszego shared.module.ts chodzi o tę linijkę:
import { MatButtonModule, MatCardModule, MatMenuModule, MatToolbarModule, MatIconModule, MatListModule, MatChipsModule, MatTableModule, MatTabsModule } from '@angular/material'; import { FlexLayoutModule } from '@angular/flex-layout';
W module „Shared module” musimy te moduły także zaimportować a potem wyeksportować w tablicy „exports”, która znajduje się pod tablicą importów. Eksport jest konieczny by inne moduły mogły ich używać. Pomyślmy sobie o tym jak o kegu z piwem:) Mamy pełny keg bo piwo jest już nalane (zaimportowane) i musimy je rozlać czyli wyeksportować.
Wracając do naszego app.module.ts importujemy nasz moduł SharedModule (SharedModule to klasa w shared.module.ts to inaczej nazwa modułu).
Zatem moduły po tych zmianach wyglądają następująco:
app.module,ts
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { AppComponent } from './app.component'; import { SharedModule } from './shared/shared.module'; @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, BrowserAnimationsModule, SharedModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
shared.module.ts
import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { MatButtonModule, MatCardModule, MatMenuModule, MatToolbarModule, MatIconModule, MatListModule, MatChipsModule, MatTableModule, MatTabsModule } from '@angular/material'; import { FlexLayoutModule } from '@angular/flex-layout'; @NgModule({ declarations: [], imports: [ CommonModule, MatButtonModule, MatCardModule, MatMenuModule, MatToolbarModule, MatIconModule, MatListModule, MatChipsModule, MatTableModule, MatTabsModule, FlexLayoutModule ], exports: [ MatButtonModule, MatCardModule, MatMenuModule, MatToolbarModule, MatIconModule, MatListModule, MatChipsModule, MatTableModule, MatTabsModule, FlexLayoutModule ] }) export class SharedModule { }
Teraz stworzymy „Home Module” i „Home Component” gdzie umieścimy header, sidebar, body i footer aplikacji.
Zaczynamy od utworzenia w Angular CLI modułu „Home Module”. Jesteśmy w katalogu aplikacji i w konsoli wpisujemy:
ng generate module home
A nstępnie :
ng generate component home
Angular CLI wygeneruje nam katalog z modułem i komponentem.
Następnie tworzymy tak samo Dashboard i część aplikacji która będzie odpowiadała za dodane własne potrawy. Nazwijmy ją „Dish:
Zatem w konsoli wpisujemy po kolei:
ng generate module dashboard ng generate module dish ng generate component dashboard ng generate component dish
Po tych czynnościach pojawia się wstępne struktura aplikacji:
Przeniesiemy teraz nasz kod z app.component.html i app.component.scss do odpowiednio home.component.html i home.component.scss i spróbujmy uruchomić aplikację.
Pojawiają się niestety błędy które musimy skorygować, na początek musimy powiedzieć app.component.html żeby pokazywał nam home.component. W home.component.ts znajujemy sekcję:
selector: 'app-home',
i kopiujemy ‚app-home”. Następnie w app.component.html tworzymy z nazwy naszego selektora selector html’owy: czyli wpisujemy:
<app-home></app-home>
To jest własnie magia angulara, która pozwala tworzyć własne komponenty. W dalszych częściach kursu będziemy je ze sobą komunikować, powielać, karmić danymi itp.
Jednakże, kiedy chcielibyśmy ponownie uruchomić aplikację, dostaniemy błąd, który mówi nam, że Angular nie może znaleźć app-home komponentu, a to dlatego że moduł główny app.module.ts nie widzi modułu home.module.ts dlatego należy wykonać pewne kroki:
- w home.module.ts importujemy SharedModule aby mieć wszystkie moduły z Material Design
- w home.module.ts eksportujemy nasz HomeComponent bo chcemy powiedzieć, że chcemy go użyć na zewnątrz
- w app.module.ts importujemy HomeModule gdyż musimy go widzieć w głównym module oba moduły prezentują się następująco:
app.module.ts
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { AppComponent } from './app.component'; import { SharedModule } from './shared/shared.module'; import { HomeModule } from './home/home.module'; @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, BrowserAnimationsModule, SharedModule, HomeModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
home.module.ts
import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { HomeComponent } from './home.component'; import { SharedModule } from '../shared/shared.module'; @NgModule({ declarations: [HomeComponent], imports: [ CommonModule, SharedModule ], exports: [ HomeComponent ] }) export class HomeModule { }
Gdy uruchomimy aplikację, powinna bez problemu się zbudować.
Teraz tworzymy komponenty header, sidebar, body i footer w „Home Module”. Jest to dosyć żmudna robota ale przejdziemy przez wszystko po kolei:
W konsoli przechodzimy do katalogu src/app/home i za pomocą Angular CLI tworzymy nasze komponenty:
ng generate component header ng generate component body ng generate component footer
ng generate component sidebar
Gdy to zrobimy zauważmy że w module home.module.ts pojawiły się deklaracje nowoutworzonych komponentów:
declarations: [HomeComponent, HeaderComponent, BodyComponent, FooterComponent],
Przyjrzyjmy się im teraz, głownie ich selektorom: app-header, app-body, app-sidebar, app-footer. Są to tak jakby bloki z których ułożymy widok apki. Teraz poprzesuwamy trochę kodu do komponentów, głownie w plikach html i css. Poniżej przedstawię jak mają wyglądać poszczególne pliki i skupimy się potem na home.component.html.
header.component.html
<header fxLayout='row'> <mat-toolbar color="primary"> <div class="app-logo"> <h1>FoodCalc</h1> </div> <div fxFlex></div> <div> <button mat-icon-button [matMenuTriggerFor]="menu"> <mat-icon>person</mat-icon> </button> <mat-menu #menu="matMenu"> <button mat-menu-item> <mat-icon>fingerprint</mat-icon> <span>Login</span> </button> <button mat-menu-item disabled> <mat-icon>close</mat-icon> <span>Logout</span> </button> <button mat-menu-item> <mat-icon>content_paste</mat-icon> <span>Registration</span> </button> </mat-menu> </div> </mat-toolbar> </header>
header.component.scss
header { position: relative; height: 220px; background: #009688; } :host /deep/ { .mat-toolbar-single-row { height: 120px; padding: 20px 0 20px 50px; } } .app-logo { h1 { font-size: 36px; } h2 { font-size: 12px; } }
sidebar.component.html
<section class="sidebar" fxFlex="220px"> <div class="main-menu-header"> <div fxLayout='row' fxFlex="200px"> <div fxFlex="33%"> <button mat-mini-fab> <mat-icon color="primary">message</mat-icon> </button> </div> <div fxFlex="33%"> <button mat-mini-fab> <mat-icon>favorite</mat-icon> </button> </div> <div fxFlex="33%"> <button mat-mini-fab> <mat-icon>list</mat-icon> </button> </div> </div> </div> <div class="main-menu"> <mat-list> <h3 mat-subheader>Navigation</h3> <mat-list-item> <mat-icon mat-list-icon>dashboard</mat-icon> <h4 mat-line> <a href="#">Dashboard</a> </h4> </mat-list-item> <mat-list-item> <mat-icon mat-list-icon>featured_play_list</mat-icon> <h4 mat-line> <a href="#">Moja lista produktów</a> </h4> </mat-list-item> <mat-list-item> <mat-icon mat-list-icon>wb_sunnyt</mat-icon> <h4 mat-line> <a href="#">Produkty sezonowe</a> </h4> </mat-list-item> <mat-divider></mat-divider> <h3 mat-subheader>Notes</h3> <mat-list-item> <mat-icon mat-list-icon>note</mat-icon> <h4 mat-line> <a href="#">Notatki</a> </h4> </mat-list-item> </mat-list> </div> </section>
sidebar.component.scss
:host /deep/ .mat-list { margin-top: 65px; } :host /deep/ .mat-list-item .mat-list-item-content { padding: 0px; }
body.component.html
<mat-card class="example-card"> <mat-card-header> <div mat-card-avatar> <mat-icon mat-list-icon>accessibility</mat-icon> </div> <mat-card-title>Mój bilans dnia</mat-card-title> <mat-card-subtitle> <mat-chip-list> <mat-chip>Kalorie: 2500</mat-chip> <mat-chip>Węglowodany: 120</mat-chip> <mat-chip color="primary" selected="true">Białko: 23</mat-chip> <mat-chip color="accent" selected="true">Tłuszcze: 340</mat-chip> </mat-chip-list> </mat-card-subtitle> </mat-card-header> <mat-card-content> <p> Wybierz porę posiłku i dodawaj produkty. Licz kalorie i sprawdz swój dzieny bilans składnoków odżywczych. </p> <mat-tab-group> <mat-tab label="Śniadanie">Content 1</mat-tab> <mat-tab label="Drugie śnadanie">Content 2</mat-tab> <mat-tab label="Obiad/Lunch">Content 2</mat-tab> <mat-tab label="Podwieczorek">Content 2</mat-tab> <mat-tab label="Kolacja">Content 2</mat-tab> <mat-tab label="Przegryzka">Content 2</mat-tab> </mat-tab-group> </mat-card-content> </mat-card>
footer.component.html
<footer fxLayout='row'> <mat-toolbar color="primary"> Footer works </mat-toolbar> </footer>
footer.component.scss
footer { position: fixed; bottom: 0; width: 100%; }
i wreszcie nasz home.component.html
<app-header></app-header> <section id="main" class="main" fxLayout='row'> <app-sidebar></app-sidebar> <section class="content" fxFlex> <app-body></app-body> </section> </section> <app-footer></app-footer>
home.component.scss
.main { padding:0 30px 0 66px; margin-top: -75px; } .content { padding: 0 0 0 30px; }
Jak widzimy w ostatnim pliku tj. home.component.html, jest tu struktura naszej aplikacji jak gdyby zbudowana z klocków, które są blokami kodu w osobnych komponentach. Zwróćny uwagę na pliki scss w komponentach. Style te dotyczą tylko komponentu w którym są, ewentualnie w komponentach podrzędnych. Jest to bardzo dobre wyjście i naprawde pomaga w utzrymaniu takiej aplikacji, zapobiega chociażby nadpisywaniu stylów.
Na tym etapie mamy już prawie całą strukturę i prezentuje sie ona tak jak na obrazku:
W następnym wpisie napiszemy serwis do tłumaczeń i sprawimy, że aplikacja będzie wielojęzykowa.
Zapraszam do komentowania i dzielenia się spostrzeżeniami. Gdyby pojawiły się pytania, napisz:)
Dzisiejsza wersja aplikacji znajduje się pod adresem : https://github.com/taaaniel/foodCalc/tree/part_5_component_modules_end
Notatki:
- https://angular.io/cli – Angular CLI komendy
Źródła
- https://angular.io/guide/displaying-data – dokumentacja komponentów
- https://angular.io/guide/ngmodules – dokumentacja modułów
Najnowsze komentarze