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)

Każda aplikacja musi posiadać jeden root module czyli moduł główny.
  • 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:

Źródła

Zostaw komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *