Angular si zasloužil moji plnou pozornost v roce 2012, kdy jsem si uvědomil důležitost oddělení frontendových a backendových záležitostí. Servery-renderované šablony jsou téměř nemožné udržovat a jsou kořenem mnoha nákladných přepracování softwarových systémů. Pokud se staráte o tvorbu udržitelného softwaru, měli byste se řídit základním pravidlem: oddělit logiku obchodní aplikace, která běží za API, od prezentační logiky implementované v uživatelském rozhraní. Tento přístup je klíčem k dlouhodobé udržitelnosti jakékoli aplikace.

Angular dokonale zapadá do principu Pareto nebo pravidla 80/20. Stal se vyspělou a neustále se vyvíjející platformou, která vám umožňuje dosáhnout 80 % úkolů s pouhými 20 % úsilí. Jak bylo zmíněno v předchozí části, každé hlavní vydání je podporováno po dobu 18 měsíců, což vytváří kontinuum učení, zůstávání v obraze a vyřazování starých funkcí. Z perspektivy full-stack vývojáře je toto kontinuum neocenitelné, protože vaše dovednosti a školení zůstanou relevantní a čerstvé po mnoho let.

Filozofie, na které Angular staví, je vsadit na konfiguraci místo konvence. I když frameworky založené na konvencích mohou na první pohled působit elegantně, ve skutečnosti ztěžují novým vývojářům jejich osvojení. Frameworky orientované na konfiguraci se snaží zpřístupnit svou vnitřní strukturu prostřednictvím explicitní konfigurace a hooků, kde můžete připojit své vlastní chování k samotnému frameworku. V podstatě, kde AngularJS měl spoustu "kouzel", které mohly být matoucí, nepředvídatelné a těžko laditelné, Angular se snaží být co nejméně "magický". To znamená, že výsledný kód je verbose – což je ve skutečnosti výhoda. Stručný kód je nepřítel udržitelnosti, neboť přináší prospěch pouze původnímu autorovi. Jak Andy Hunt a David Thomas uvedli v knize The Pragmatic Programmer: "Pamatujte si, že kód budete číst mnohem častěji, než ho budete psát."

Verzní, oddělený, koherentní a enkapsulovaný kód je klíčem k budoucí odolnosti vašeho software. Angular umožňuje správné vykonávání těchto principů pomocí různých mechanismů. Eliminuje mnoho vlastních konvencí vynalezených v AngularJS, jako je například ng-click, a zavádí přirozenější jazyk, který staví na stávajících HTML prvcích a jejich vlastnostech. Výsledkem je, že ng-click se mění na (click), čímž Angular rozšiřuje HTML místo jeho nahrazování.

Pojďme se nyní podívat na koncept "evergreenu" v Angularu a na reaktivní programovací paradigma, které jsou posledními rozšířeními původní filozofie Angularu.

Angular Evergreen

Když se učíte Angular, neucíte se pouze jednu konkrétní verzi Angularu, ale platformu, která se neustále vyvíjí. Během let, co se věnuji tomuto tématu, jsem měl několik rozhovorů s členy týmu Angular a mysliteli v komunitě, a vyslechl jsem mnoho prezentací. Na základě těchto zkušeností můžete spoléhat na Angular jako na zralou platformu pro webový vývoj. Angular pravidelně dostává aktualizace s velkým důrazem na zpětnou kompatibilitu. Každý kód, který se stane nekompatibilní s novou verzí, je přizpůsoben s pomocí automatizovaných nástrojů nebo explicitního vedení při aktualizaci kódu, což je možné nalézt v Angular Update Guide na adrese https://angular.dev/update. Takto se nikdy nebudete muset obávat, že budete hledat odpovědi na internetových fórech.

Tento přístup umožňuje vývojářům věnovat se neustálému vývoji a nestát na místě. Vznikla i VS Code rozšíření Angular Evergreen, které detekuje aktuální verzi Angularu a porovnává ji s nadcházejícími verzemi. Tato verze je určena pro testování kompatibility s novými funkcemi, ale není vhodná pro produkční nasazení.

Typografie Angularu

Jedním z klíčových prvků Angularu, který umožňuje této platformě zůstat evergreen, je TypeScript. TypeScript umožňuje efektivní implementaci nových funkcí při současné podpoře starších prohlížečů, což znamená, že váš kód může dosáhnout co nejširšího publika. TypeScript je jazyk vytvořený Anderse Hejlsbergem, autorem C# a Delphi, s cílem řešit problémy s použitím JavaScriptu na velkých podnicích. Tento jazyk přináší výhody staticky typovaných jazyků jako Java nebo C#, což znamená, že chyby lze zachytit již během kompilace, což je mnohem levnější než při běhu aplikace.

TypeScript, na rozdíl od tradičního kompilátoru, není kompilátor v pravém slova smyslu. Je to transpiler, který převádí kód z jednoho dialektu na jiný. Výsledkem je čistý JavaScript, který lze spustit v jakémkoli prohlížeči. TypeScript je kompatibilní i s novými funkcemi ECMAScript, které jsou zpětně kompatibilní s historickými verzemi prohlížečů. Tento proces, označovaný jako transpile, je klíčovým nástrojem pro vývojáře, kteří chtějí psát produktivně, ale zároveň zajistit, aby jejich aplikace fungovala ve všech prohlížečích.

Architektura komponent v Angularu

Angular využívá pattern MV*, což je hybrid mezi MVC a MVVM. Základní architektura obou těchto vzorců je velmi podobná. Novým konceptem je ViewModel, který představuje lepidlo spojující zobrazení s modelem nebo službou. V Angularu je toto "lepidlo" známé jako binding. Zatímco v tradičních MVC rámcích tento vztah bývá implicitní, Angular klade důraz na explicitní deklaraci tohoto spojení, což přináší lepší kontrolu nad aplikací a usnadňuje její údržbu.

Udržitelnost kódu a kvalitní struktura aplikace jsou základem pro její dlouhodobý úspěch. A právě Angular poskytuje vývojářům nástroje, jak tohoto cíle dosáhnout, a to jak díky přístupu k verzím a správě aktualizací, tak i díky použití TypeScriptu a přístupu k architektuře aplikace.

Jak správně řešit změny v aplikacích pomocí ngOnInit, API volání a validace poštovních kódů?

V moderních webových aplikacích, zvláště při práci s frameworky jako Angular, je kladeno velké důraz na správu životního cyklu komponent a efektivní práci s asynchronními operacemi. Jedním z těchto klíčových procesů je detekce změn, která se může stát zdrojem problémů, pokud není správně implementována. V tomto kontextu je velmi důležité mít správné pochopení, kdy a jak používat metodu ngOnInit. I když to vypadá, že existují případy, kdy by bylo vhodné volat metodu mimo tento životní cyklus, v 99 % případů byste měli zůstat u ngOnInit.

Proč je tomu tak? Důvod je jednoduchý: ngOnInit je volán při inicializaci komponenty a je ideálním místem pro spouštění jakýchkoli operací, které závisí na stavu komponenty. To zajistí, že veškeré proměnné a závislosti, které jsou potřeba pro správné fungování funkce, jsou již správně nastaveny. Tato metoda se neprovádí během změn, což zajišťuje stabilitu a predikovatelnost výsledků.

Pokud vezmeme jako příklad funkci, která provádí vyhledávání, tak při jejím volání v kontextu funkce, jako je doSearch, máme lepší kontrolu nad tím, jaké hodnoty se do ní dostanou. V takovém případě je funkce izolována od stavu třídy a jedná se o čistou, testovatelnou jednotku. To znamená, že je mnohem snazší ji otestovat a ujistit se, že funguje správně, aniž bychom se museli zabývat složitým stavem aplikace.

Podobně v případě s formuláři, které využívají Observable, je důležité nezapomenout na volání subscribe(). Bez něj nebude data tok aktivován, a tudíž se neprovede žádná akce. Na druhou stranu implementace v rámci ngOnInit ukazuje skutečnou asynchronní povahu kódu, který je nezávislý na životním cyklu aplikace, ale stále je doporučeno držet se této metody jako nejlepší praxe.

Pokud se podíváme na změny, které se objevily v aplikaci po refaktoringu, můžeme vidět, že aplikace nyní funguje stejně jako dříve, ale s výrazně zjednodušeným kódem, což usnadňuje jeho údržbu a rozšiřování.

Další výzvou při vývoji aplikací je práce s poštovními kódy. V aktuální podobě naše aplikace podporuje pouze 5místné poštovní kódy z USA. Nicméně, poštovní kódy se mohou výrazně lišit v závislosti na zemi. Příklad Velké Británie ukazuje, jak rozdílný může být formát poštovních kódů (například EC2R 6AB), což komplikuje validaci. Uživatelé si dnes na webových aplikacích žádají vysokou úroveň tolerance vůči chybám, takže nemůžeme počítat s tím, že každý uživatel zadá poštovní kód správně.

Pro tuto situaci je nezbytné využít externí službu pro validaci poštovních kódů před tím, než provede aplikace volání na API, například OpenWeatherMap. Zde přichází na scénu možnost řetězení API volání. K tomu, abychom mohli ověřit platnost poštovního kódu, použijeme službu od poskytovatele jako je geonames.org.

Pro správné zpracování poštovního kódu je nezbytné provést několik kroků. Za prvé, je potřeba registrovat účet na geonames.org a v konfiguraci aplikace uchovávat přihlašovací údaje. Dále vytvoříme rozhraní pro službu poštovních kódů, které se bude starat o validaci a komunikaci s API. Toto rozhraní umožňuje aplikaci získávat informace o poštovním kódu a podle těchto údajů následně rozhodovat, jakým způsobem bude aplikace pokračovat.

Dalším krokem je implementace služby, která bude využívat API Geonames pro získání informací o poštovním kódu. Tento proces zahrnuje vytvoření HTTP požadavku s parametry jako maximální počet řádků a přihlašovací údaje. Výsledek se následně zpracuje a vrátí zpět do aplikace. Pokud API nenalezne žádný výsledek, zajistí funkce defaultIfEmpty, že se vrátí hodnota null.

Když již máme tuto službu na místě, můžeme ji začlenit do našeho hlavního výpočtu, například pro zjištění aktuálního počasí na základě souřadnic zadaných uživatelem. Tento proces se nazývá sekvenční zpracování Observable, kde se volání na službu poštovního kódu řetězí s voláním na API počasí pomocí funkce switchMap. To nám umožňuje efektivně zpracovávat požadavky, aniž bychom museli čekat na odpověď z jednoho volání před tím, než provedeme další.

Tento přístup nejen zjednodušuje kód, ale také umožňuje aplikaci fungovat na globální úrovni, což je důležité pro moderní webové aplikace, které musí být robustní a přizpůsobivé vůči uživatelským chybám.

V rámci testování a udržitelnosti kódu je dobré využívat principy TDD (Test-Driven Development). Definování rozhraní a jejich použití při psaní testů umožňuje zajistit, že aplikace bude správně fungovat i při změnách nebo rozšířeních.

Důležité je také pochopit, že při práci s asynchronními operacemi, API voláními a externími službami je nezbytné dbát na to, aby byl kód čistý, dobře strukturovaný a snadno testovatelný. Používání správných životních cyklů komponent, správné organizování asynchronních operací a promyšlené zpracování chyb zajišťují, že aplikace bude stabilní a uživatelsky přívětivá.

Jak implementovat moduly POS a inventáře v aplikaci založené na Angularu?

Pro správu aplikace typu "walking skeleton" musíme zajistit, aby manažer měl přístup ke všem klíčovým modulům, které budeme vytvářet. K tomu je nezbytné poskytnout manažerovi možnost přístupu k modulům POS (Point of Sale) a inventáře. Začněme tím, že do komponenty manažera přidáme dvě nové tlačítka pro navigaci k těmto modulům.

V souboru manager.component.ts přidáme nové odkazy na routy:

typescript
list
shopping_cart

Je třeba si uvědomit, že tyto odkazy nás vyvedou mimo rámec modulu ManagerModule, což znamená, že sekundární panel nástrojů specifický pro manažera zmizí. Jakmile to nastavíme, bude na nás, abychom implementovali dva zbývající moduly: POS a inventář.

Modul POS (PosModule)

Modul POS je velmi podobný modulu pro správu uživatelů, tedy UserModule, ale s tím rozdílem, že PosModule bude mít výchozí cestu. PosComponent bude výchozí komponentou tohoto modulu. Tento modul může být poměrně komplikovaný, protože bude obsahovat několik podkomponent, proto bychom se měli vyhnout používání inline šablon a stylů. Implementace modulu POS zahrnuje několik klíčových kroků:

  1. Vytvořte komponentu PosComponent.

  2. Zaregistrujte PosComponent jako výchozí cestu.

  3. Nakonfigurujte lazy loading pro PosModule.

  4. Zajistěte, aby aplikace správně fungovala.

Modul Inventáře (InventoryModule)

Modul Inventáře je podobný modulu manažera, jak je znázorněno na následujícím diagramu. Proces implementace modulu inventáře se skládá z těchto kroků:

  1. Vytvořte základní komponentu pro inventář.

  2. Zaregistrujte MaterialModule pro podporu materiálových komponent.

  3. Vytvořte komponenty pro domovskou stránku inventáře, záznamy o zásobách, produkty a kategorie.

  4. Nakonfigurujte rodičovské a podřízené routy v souboru inventory-routing.module.ts.

  5. Nastavte lazy loading pro InventoryModule.

  6. Implementujte sekundární panel nástrojů pro navigaci v rámci modulu Inventáře ve komponentě InventoryComponent.

  7. Zajistěte správnou funkčnost aplikace podle předdefinovaného návrhu.

Po dokončení základní kostry aplikace je důležité zkontrolovat výstup z CLI (Command Line Interface) a ujistit se, že všechny moduly nebo komponenty jsou správně lazy loaded. Je nezbytné odstranit jakékoliv chyby v testování, než přejdeme na další fázi vývoje.

Vytvoření společného testovacího modulu

Jakmile máme několik modulů, stává se neefektivní ručně konfigurovat importy a providery pro každý testovací soubor. V tomto případě je rozumné vytvořit společný testovací modul, který bude obsahovat generickou konfiguraci, kterou můžeme opakovaně použít.

Vytvoření souboru common.testing.ts umožňuje zahrnout všechny potřebné testovací moduly a providery:

typescript
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { ReactiveFormsModule } from '@angular/forms';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { RouterTestingModule } from '@angular/router/testing';
import { MatIconTestingModule } from '@angular/material/icon/testing';
export const commonTestingModules = [ ReactiveFormsModule, NoopAnimationsModule, HttpClientTestingModule, RouterTestingModule, MatIconTestingModule, ] as unknown[];

Tento přístup nám ušetří čas a zjednoduší testování. Nicméně, jak aplikace roste, je důležité být opatrný, abychom neimportovali zbytečné moduly, které zpomalují běh testů.

Design datových entit

V dalším kroku je důležité definovat hlavní datové entity, kolem kterých budeme stavět naše API. Tento návrh by měl zahrnovat Entity Relationship Diagram (ERD), který nám pomůže pochopit vztahy mezi entitami. Tento diagram slouží jako základ pro vytváření CRUD API (Create, Read, Update, Delete) pro každou entitu.

Při navrhování dat je vhodné zvážit, zda vaše databáze bude SQL nebo NoSQL. Pokud začínáte od nuly, MongoDB je flexibilnější volbou, která se hodí pro dynamické požadavky aplikace.

Uživatelské rozhraní

Při navrhování uživatelského rozhraní je kladeno důraz na to, jak snadno mohou uživatelé vykonávat běžné úkoly. Důležité je, aby uživatelé měli přístup k veškerým informacím a funkcím aplikace bez zbytečné navigace. Je třeba zaměřit se na to, zda uživatelé mohou snadno vyhledávat požadovaná data a zda mají možnost zobrazit podrobnosti o záznamech.

Navrhování obrazovek by mělo být modulární a opakovaně použitelné. Každý uživatelský prvek nebo komponenta, která bude použita napříč aplikací, by měla být definována na úrovni kořene, zatímco ostatní by měly být specifické pro jednotlivé moduly.

Dokumentace artefaktů

Jakmile vytvoříte různé designové artefakty, jako jsou návrhy obrazovek nebo rozhodnutí týkající se designu, je důležité je zaznamenat a sdílet s celým týmem. Wiki nabízí výborný nástroj pro vytváření živé dokumentace, kterou mohou všichni členové týmu průběžně upravovat. Tímto způsobem bude zajištěno, že všechny rozhodnutí a artefakty budou přístupné a aktuální pro všechny zainteresované strany.

Jak správně implementovat Master/Detail zobrazení s tabulkami a stránkováním v Angularu

V tomto článku se zaměříme na implementaci komplexního řešení pro Master/Detail zobrazení v aplikacích postavených na Angularu. Tento přístup zahrnuje práci s datovými tabulkami, stránkováním a filtrováním, a také ukážeme, jak efektivně využít RxJS pro práci s asynchronními daty. Celkový cíl je vytvořit efektivní a intuitivní uživatelské rozhraní pro správu uživatelských dat pomocí komponent a služeb Angularu.

Pokud bychom se vrátili k předchozímu scénáři, zjistíme, že správná definice master outletu je klíčová pro správné zobrazení dat. Například, když je master outlet explicitně definován jako ['../users', { outlets: { master: [''], detail: ['user', {userId: row.id}] } }], mohlo by to způsobit, že router správně nezpracuje tuto trasu a data se nezobrazí. Správně fungující implementace by měla mít prázdné pole pro master outlet, tj. master: [], což zajišťuje správné přiřazení cesty.

Pokud již máme implementovanou ochranu pomocí resolve pro komponentu ViewUserComponent, můžeme v nástrojích pro vývojáře Chrome (Chrome DevTools) ověřit, zda jsou data správně načítána. Před tím, než začneme ladit, ujistěte se, že server, který jsme vytvořili v předchozím kapitole pro práci s REST a GraphQL API, běží. Poté nastavíme bod přerušení hned po přiřazení hodnoty do this.currentUser ve ViewUserComponent a ověříme, že data jsou načítána správně, aniž by bylo třeba psát složitý kód pro načítání dat v metodě ngOnInit.

Další krok spočívá v implementaci hlavního zobrazení (master view), které bude zahrnovat tabulku uživatelů s podporou stránkování. Pro tuto implementaci vytvoříme komponentu UserTableComponent, která bude obsahovat vlastnost dataSource typu MatTableDataSource. Tato komponenta bude odpovědná za zobrazení dat v tabulce s podporou stránkování, filtrování a třídění.

Prvním krokem je implementace rozhraní IUsers, které popisuje strukturu dat pro stránkovaný seznam uživatelů. V tomto případě bude rozhraní obsahovat dvě hlavní vlastnosti: data, což je pole objektů typu IUser, a total, což je celkový počet uživatelů, které můžeme načíst. Na základě těchto dat bude komponenta UserTableComponent schopna správně vykreslit tabulku a provádět stránkování.

Pro správné načítání dat z REST API musíme aktualizovat rozhraní služby UserService o metodu getUsers, která přijímá parametry pro stránkování, filtrování a třídění. Tato metoda bude volat API endpoint a získávat data pro uživatele na základě těchto parametrů. Parametry zahrnují velikost stránky (pageSize), text pro vyhledávání (searchText), počet přeskočených stránek (pagesToSkip), a také možnost seřazení podle určitého sloupce (sortColumn) a směru řazení (sortDirection). Tento přístup umožňuje aplikaci efektivně manipulovat s velkým množstvím dat.

V komponentě UserTableComponent bude nakonfigurováno stránkování, třídění a filtrování pomocí komponent Angular Materialu jako je MatPaginator a MatSort. Tato komponenta bude obsahovat několik užitečných vlastností a metod, které umožní dynamické zobrazení dat v tabulce. Pomocí těchto funkcí můžeme definovat dynamické chování tabulky, jako například skrytí nebo zobrazení sloupců v závislosti na uživatelských preferencích nebo dynamickém nastavení aplikace.

Navíc pro filtrování dat využíváme pole pro vyhledávání, které umožňuje uživatelům zadávat text a filtrovat zobrazené výsledky podle jména nebo e-mailu. Tento vstup je propojený s FormControl, což nám poskytuje nástroj pro validaci a změnu hodnoty filtru. Po každé změně hodnoty hledání se stránkování vrátí na první stránku, aby bylo zajištěno správné zobrazení výsledků.

Když uživatel klikne na řádek v tabulce, zavoláme metodu showDetail, která otevře detailní zobrazení vybraného uživatele v přiděleném outletu. Tento přístup je flexibilní a umožňuje efektivně spravovat různé pohledy a stav aplikace bez zbytečných reloadů stránky nebo komplikovaných přesměrování.

Důležité je si uvědomit, že tento způsob práce s daty není jen o implementaci konkrétního rozhraní nebo řešení. Když pracujete s velkými objemy dat a složitějšími požadavky na výkon, musíte vždy zvážit možnosti optimalizace, například použití paginace nebo lazy loadingu dat. Navíc je kladeno důraz na správné zacházení s asynchronními operacemi, což výrazně ovlivňuje celkový uživatelský zážitek a výkonnost aplikace.