IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

Lekce 3 - Dokončení kalkulačky v Angular frameworku

V minulé lekci, První aplikace v Angular frameworku, jsme započali tvorbu jednoduché kalkulačky ve frameworku Angular.

V dnešním tutoriálu se budeme zabývat především komponentami. Naposled jsme skončili u služeb a tak na ně navážeme Angular komponentami, takže bez dalšího otálení jdeme na to! :)

Komponenty

V rámci zachování nějaké struktury si rozdělíme práci s komponentami na definice jejich tříd, jejich šablony a samozřejmě styly v podobě CSS.

Definice

Začneme tedy implementací tříd komponent v TypeScriptu.

src/app/app.component.ts

Ze všeho nejdříve se opět podíváme na zoubek hlavní komponentě celé naší aplikace, kde je mimo jiné definován hlavní nadpis naší stránky:

import { Component } from '@angular/core';

/**
 * Hlavní komponenta celé aplikace.
 * @export
 */
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'Kalkulačka v Angular frameworku';
}

Zde už bylo vše potřebné řečeno v předchozích lekcích.

Pokračovat budeme implementací komponenty pro naši kalkulačku v rámci Angularu. Opět můžeme využít Angular CLI a nechat si základ komponenty vygenerovat jediným příkazem:

ng generate component calculator

src/app/calculator/calculator.component.ts

Jak jste si mohli všimnout např. v předešlé lekci na obrázku, základem naší aplikace je webový formulář pro kalkulačku. A právě tato komponenta bude mít na starost tvorbu a obsluhu tohoto formuláře spolu s využitím služby pro výpočty, kterou jsme minule definovali. Pojďme tedy vytvořit definici našeho formuláře do naší komponenty:

import { Component, OnInit } from '@angular/core';
import { FormBuilder, Validators } from '@angular/forms';

import { CalculatorService } from '../services/calculator.service';
import { Operation } from '../services/operation';

/**
 * Komponenta kalkulačky.
 * @export
 */
@Component({
  selector: 'app-calculator',
  templateUrl: './calculator.component.html',
  styleUrls: ['./calculator.component.css']
})
export class CalculatorComponent implements OnInit {
  /** Výsledek operace nebo null. */
  result: number | null = null;

  /** Formulář kalkulačky s jeho sestavením. */
  calculatorForm = this.formBuilder.group({
    x: [0, Validators.required],
    y: [0, Validators.required],
    operation: ['', Validators.required]
  });

  /**
   * Konstruktor s injektovanou službou pro sestavování formulářů a pro práci s operacemi kalkulačky.
   * @param formBuilder automaticky injektovaná služba pro sestavování formulářů
   * @param calculatorService automaticky injektovaná služba pro práci s operacemi kalkulačky
   */
  constructor(private formBuilder: FormBuilder, private calculatorService: CalculatorService) { }

  /** Getter pro existující operace kalkulačky. */
  get operations() { return Operation; }

  /** Funkce vykonaná při úspěšném odeslání formuláře kalkulačky, která zpracuje odeslané hodnoty. */
  onSubmit(): void {
    const values = this.calculatorForm.value; // Získání odeslaných hodnot z formuláře.
    const operation: keyof typeof Operation = values.operation as keyof typeof Operation;
    // Necháme si vypočítat výsledek podle zvolené operace a zadaných hodnot.
    // Funkce calculate() očekává hodnoty x a y typu number. Hodnoty x a y tedy převedeme pomocí funkce Number().
    this.result = this.calculatorService.calculate(Operation[operation], Number(values.x), Number(values.y));
  }

  ngOnInit(): void { }
}

Zde bych se již na chvíli zastavil. Nejdříve si povšimněte konstruktoru třídy, kde se aplikuje princip Dependency Injection (DI), která nám umožňuje předávat služby napříč naší aplikací. Ta v Angularu funguje opravdu skvěle, když v komponentě potřebujeme používat nějakou službu, jednoduše si jí necháme frameworkem předat. Stačí ji definovat jako parametr v rámci konstruktoru a je hotovo.

Dále se zde definují atributy, které budou zároveň fungovat v šabloně komponenty. To samé platí i u metod, které poté můžeme volat jako akce ze šablon. Pokud tedy opět jednoduše definujeme veřejný atribut třídy, bude dostupný jako proměnná i v šabloně a zároveň spolu budou provázány - při změně jednoho dojde ke změně i u druhého. Ostatně to za chvíli uvidíte sami :)

Takže tedy jako atributy zde definují výsledek operace result, který budeme zobrazovat a dále pak samotný formulář calculatorForm. Ten je zde sestavován pomocí speciální služby, která souvisí s modulem pro reaktivní formuláře z minulé lekce. Pomocí ní jsou definovány jednotlivá pole formuláře i se svými validačními pravidly. Dále je zde ještě getter operations(), který vrací seznam všech dostupných operací. Zpracování formuláře pak řeší samostatná metoda onSubmit(), kterou budeme volat při jeho odeslání a je detailně zdokumentována již v kódu :)

V Angularu obecně existují hned dva nezávislé způsoby, jak pracovat s webovými formuláři - reaktivní a šablonovací. K tomu se ještě dají u obou řešit různé věci trochu jinými způsoby, takže celkově by se tento formulář dal napsat více než dvěma naprosto validními alternativami :) Dále v kurzu si je ukážeme.

Šablony

Pokračovat budeme doplněním implementace šablon pro naše komponenty, což je vlastně mix hlavně HTML a Angular kódu.

src/app/app.component.html

Začneme opět šablonou pro hlavní komponentu naší aplikace. Obsah této šablony vymažeme a upravíme následovně:

<div style="text-align: center;">
  <h1>{{ title }}</h1>
  <app-calculator></app-calculator>
</div>

Vidíte, že zde pouze pozměníme titulek a místo obsahu vložíme naši komponentu kalkulačky. Nic víc zde není potřeba.

src/app/calculator/calculator.component.html

Dále budeme pokračovat se šablonou naší komponenty pro kalkulačku:

<form [formGroup]="calculatorForm" (ngSubmit)="onSubmit()">
  <label for="x">
    První číslo: <input id="x" type="number" required formControlName="x">
  </label>
  <label for="y">
    Druhé číslo: <input id="y" type="number" required formControlName="y">
  </label>
  <label for="operation">
    Operace:
    <select id="operation" required formControlName="operation">
      <option value="">--Vyberte operaci--</option>
      <option *ngFor="let operation of operations | keyvalue" [value]="operation.key">{{ operation.value }}</option>
    </select>
  </label>
  <input type="submit" [disabled]="!calculatorForm.valid" value="Spočítej">
</form>
<div id="result" [hidden]="result == null">Výsledek je: {{ result }}</div>

Pojďme si tento kód zas trochu rozebrat. Jeho základ tvoří HTML formulář, to by mělo být jasné. Ten je provázaný na formulář definovaný v naší komponentě pomocí direktivy formGroup, díky které pak máme přístup k jeho polím. Ty jsou potom svázány s jejich HTML definicí pomocí formControlName.

Následně je zde vidět také mapování odeslání formuláře (ngSubmit) na metodu naší komponenty onSubmit().

Další věcí je generování možností pro HTML <select> pomocí *ngFor, které je vytváří přímo z předaných hodnot komponenty.

Za zmínku také stojí dynamická validace formuláře na tlačítku pro jeho odeslání, která ho znepřístupní v případě, že je formulář nevalidní.

Na konci je poté vidět zobrazení výsledku pomocí standardního frameworkového zápisu {{ ... }}. Výsledek bude vidět, pouze pokud je result jiný než null. Ten se díky Angularu dynamicky mění v závislosti na aktuálním výpočtu.

Styly

Tímto je práce na šablonách hotova, ale my si ještě na závěr doplňme CSS k našim komponentám, aby alespoň trochu vypadaly.

src/app/calculator/calculator.component.css

Zde bude stačit styl k naší komponentě kalkulačky, protože u hlavní komponenty není v podstatě co stylizovat:

:host {
    display: flex;
    flex-direction: column;
    align-items: center;
}

label {
    display: block;
    clear: both;
    margin: 10px 0;
}

label > input {
    float: right;
    margin-left: 5px;
}

input[type="submit"] {
    display: block;
    margin: 0 auto;
}

#result {
    font-size: 1.5em;
    font-weight: bold;
    margin: 10px 0;
}

O CSS v Angularu je zde třeba říct dvě věci. Za prvé, všechny styly jsou izolované v rámci jednotlivých komponent. Tzn. že zdejší styl pro label nijak neovlivní tento element uvnitř jiných komponent.

A druhá věc, pokud chceme naopak ovlivňovat alespoň částečně okolní CSS, musíme použít speciální angularovské notace jako např. uvedený :host. Ten zařídí, aby naše komponenta jako taková tj. <app-calculator>, měla nějaký výchozí styl.

A tím je naše práce na celém projektu dokončena. Pokud se teď podíváte na příslušnou URL, měli byste vidět funkční kalkulačku a můžete na ní vyzkoušet všechny chytáky, které vás napadnou :)

Pokud vám není cokoli jasné, stáhněte si projekt z přílohy a projeďte si ho. Kódu v aplikaci tolik nemáme, po chvíli byste se v principu měli zorientovat.

V příloze nejsou nainstalované moduly, kvůli jejich velikosti. Je tedy potřeba před spuštěním aplikace použít příkaz npm install.

To je pro tuto lekci opravdu vše.

V další lekci, Databázový klient v Angular - Příprava projektu, si připravíme adresářovou strukturu projektu, stáhnem hotový kód serveru a šablonu Angular klienta.


 

Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.

Stáhnout

Stažením následujícího souboru souhlasíš s licenčními podmínkami

Staženo 227x (214.53 kB)
Aplikace je včetně zdrojových kódů v jazyce JavaScript

 

Předchozí článek
První aplikace v Angular frameworku
Všechny články v sekci
Základy Angular frameworku
Přeskočit článek
(nedoporučujeme)
Databázový klient v Angular - Příprava projektu
Článek pro vás napsal Jindřich Máca
Avatar
Uživatelské hodnocení:
29 hlasů
Autor se věnuje převážně webovým technologiím, ale má velkou zálibu ve všem vědeckém, nejen ze světa IT. :-)
Aktivity