Komponentová architektura v Angularu se opírá o dědičnost a metadata, která propojují TypeScriptové třídy s DOM strukturou skrze šablony a datovou vazbu. Tím se stává možné nejen sdílení logiky mezi komponentami, ale také sdílení metadat bez potřeby duplikovat konstruktory. Dědičnost komponent v Angularu umožňuje vytvářet rozšiřující varianty základních komponent s minimem kódu – například komponenta SuggestedSearchComponent rozšiřuje základní BaseSearchComponent, přidává vstupní vlastnost suggestions a využívá stejné styly i události. Tato architektura výrazně zvyšuje opakovatelnost, údržbu a čitelnost kódu.

V kontextu Angular Ivy se tento princip dědičnosti rozšiřuje i na úroveň metadat – tzv. metadata-enabled inheritance. Ivy je nejen novou verzí kompilátoru a runtime, ale také zásadní změnou v tom, jak Angular interpretuje komponenty. Systém Ivy Instruction Set nahrazuje předchozí View Engine a umožňuje převod komponenty do dvou seznamů instrukcí: jeden inicializuje DOM, druhý provádí aktualizace při změnách stavu. Tento přístup eliminuje potřebu překladu mezikódu jako ve View Engine a výsledkem jsou menší a rychlejší balíčky.

Dříve se stávalo, že JIT-kompilované aplikace měly menší velikost výstupu než ty kompilované pomocí AOT. S příchodem Ivy je tento rozdíl eliminován. AOT (Ahead-of-Time) kompilace se nyní uplatňuje ve všech fázích vývoje, což odstraňuje nesrovnalosti mezi prostředím pro vývoj, testování, sestavení a produkci. Navíc se tím zvyšuje spolehlivost kódu, protože chyba, která by se jinak projevila až za běhu, je odhalena během kompilace.

Stěžejním přínosem Ivy je tzv. tree-shaking – odstranění nepoužívaných částí runtime z výstupního balíčku. Pokud aplikace nevyužívá například animační nebo globalizační API, nejsou součástí finálního balíčku. To je zásadní pro mikrofrontendy a webové komponenty, kde každý bajt navíc znamená zbytečnou zátěž.

Od verze Angular 12 se doporučuje, aby knihovny byly kompilovány částečně pomocí Ivy. Tento způsob umožňuje inkrementální sestavení, čímž se výrazně zrychluje celý build. Ivy totiž ke kompilaci komponenty nepotřebuje znát detaily závislostí ostatních komponent – postačí veřejné API. Tento přístup vychází z principu lokalizace – každý modul je kompilován izolovaně.

Vývojáři však musí počítat s delším časem prvotního buildu, pokud aplikace závisí na knihovnách kompilovaných pomocí staršího View Engine. Angular Compatibility Compiler (ngcc) musí převést každou knihovnu ve složce node_modules, což se obtížně spravuje v CI/CD pipelinech. Proto je zásadní přechod na Ivy i pro třetí strany.

Další zásadní přínos nové generace Angularu spočívá v podpoře tzv. strict módu. Od verze 12 je ve výchozím nastavení ng new zapnut parametr --strict, který aktivuje pokročilou statickou analýzu a nastaví přísnější TypeScriptové kompilátorové volby. Mezi ně patří např. noImplicitAny, strictNullChecks či strictPropertyInitialization. Díky nim lze odhalit celou řadu potenciálních chyb už při sestavení, čímž se snižuje riziko runtime chyb. Tento přístup posiluje důvěru v typový systém a zvyšuje kvalitu výstupního kódu.

Tyto možnosti však nejsou pouhými optimalizacemi – reflektují nový přístup ke konstrukci frontendových aplikací. Angular už není pouze frameworkem s přednastavenou strukturou a silnými nástroji, ale také platformou, která aktivně využívá možnosti moderního JavaScriptu, TypeScriptu a nástrojového řetězce.

Důležité je si uvědomit, že Ivy není jen technickou změnou, ale filozofií – odděluje kompilaci od běhu, zefektivňuje vývojový cyklus a posiluje modularitu aplikací. Vývojáři by se měli naučit přemýšlet o aplikaci jako o sadě izolovaných komponent s jasně definovanými rozhraními. Struktura kódu, přehlednost a čistota závislostí se stávají klíčovými atributy úspěšného projektu.

Jak Ivy zlepšuje vývojářskou zkušenost v Angularu?

V posledních letech se Angular, jedním z nejpopulárnějších JavaScriptových frameworků, dočkal významných vylepšení, která zásadně ovlivňují produktivitu vývojářů. S příchodem Ivy, nového renderovacího enginu, byly provedeny zásadní změny, které nejen zjednodušují práci s Angular projekty, ale i přinášejí nové nástroje pro efektivní vývoj a diagnostiku. Tato kapitola se zaměřuje na konkrétní vylepšení, která Ivy přináší, a ukazuje, jakým způsobem mohou tyto změny zefektivnit práci s Angular aplikacemi.

V Ivy došlo k zásadnímu zlepšení chybových hlášení, která nyní poskytují podrobnější informace o problémech v kódu. Například při chybných deklaracích v poli imports aplikace, jako je uvedeno v následujícím příkladu, se nová verze Ivy nezastaví pouze u obecného popisu chyby, ale rovnou ukáže konkrétní místo a typ chyby:

pgsql
ERROR in projects/second-app/src/app/app.module.ts:11:3 - error TS2322: Type '(typeof CommonModule | typeof BrowserModule | undefined)[]' is not assignable to type '(any[] | Type | ModuleWithProviders<{}>)[]'.

V předchozím příkladu vidíme, že chyba je spojena s hodnotou undefined, která byla neúmyslně přidána do pole imports. Ivy zde poskytuje konkrétní a srozumitelné informace o problému, což výrazně usnadňuje jeho opravu.

Dalším příkladem zlepšení je chybová hláška týkající se situací, kdy není možné staticky určit hodnotu šablony komponenty. Představme si komponentu, která používá dynamickou hodnotu jako šablonu, například:

typescript
import { Component } from '@angular/core'; const template = location.href; @Component({ selector: 'app-root', styleUrls: ['./app.component.css'], template, }) export class AppComponent {}

Ve verzi View Engine se taková chyba zobrazovala jako:

pgsql
ERROR in No template specified for component AppComponent

Tento typ hlášky nám pouze říkal, že šablona není zadána, ale neobjasnil, proč tomu tak je. Ivy však poskytuje mnohem detailnější chybovou zprávu:

vbnet
ERROR in src/app/app.component.ts:8:3 - error NG1010: template must be a string

Tato zpráva je nejen podrobnější, ale i užitečnější, protože přesně ukazuje, že hodnota location.href není staticky determinovatelná a že se jedná o externí deklaraci, kterou není možné vyhodnotit během kompilace.

Jedním z významných vylepšení, které Ivy přináší, je přísnější typová kontrola šablon. Zatímco View Engine měl pouze základní a plný režim kontroly typů, Ivy zavádí přísnou kontrolu typů v šablonách. To znamená, že všechny vazby a proměnné v šablonách jsou nyní důkladně kontrolovány, včetně bezpečnostních operací pro null a undefined hodnoty, generických typů pro komponenty a direktivy, a také typů pro referenční proměnné a atributy DOM elementů. Tento přísný přístup je důležitý například pro správnou práci s AsyncPipe, který ve své počáteční fázi vyzařuje hodnotu null. Pokud je tato hodnota použita ve vazbě, je nutné zajistit, aby typy vstupních vlastností zahrnovaly možnost null, nebo použít známé mechanismy ochrany šablon a náznaky typu pro vstupní settery.

Další oblastí, která byla v Ivy výrazně vylepšena, je proces aktualizace Angular aplikace. Příkaz ng update nyní umožňuje automatické migrace, které usnadňují přechod mezi verzemi. Každá migrace je doprovázena zprávou, která informuje o provedených změnách, a například upozorní na odstranění statického flagu v dynamických dotazech. Tento typ vylepšení je obzvláště užitečný pro vývojáře, kteří musí často aktualizovat své aplikace na novější verze Angularu. Dalším zajímavým vylepšením je možnost vytváření commitů pro každou migraci pomocí parametru --create-commits, což umožňuje lépe sledovat a ladit proces aktualizace.

Ivy také přináší výrazná vylepšení do nástrojů pro práci s kódem, zejména s Angular Language Service, který poskytuje lepší integraci s vývojovými prostředími, jako je například VS Code. Tento nástroj nyní umožňuje kontrolovat platnost URL adres šablon inline, což výrazně usnadňuje například přejmenovávání komponent. Kromě toho je možné snadno přecházet mezi šablonami a souvisejícími soubory stylů pomocí příkazu "Go to definition", což urychluje navigaci v kódu. V Ivy verzi Angular Language Service jsou také dostupné další kontextové informace v tooltipu, které ukazují, jaké moduly deklarují jednotlivé komponenty.

V souhrnu, Ivy přináší řadu vylepšení, která nejen zjednodušují práci s Angular aplikacemi, ale také zvyšují produktivitu vývojářů. Od podrobných chybových hlášení přes přísnou kontrolu typů až po vylepšenou integraci s IDE, všechny tyto změny znamenají výrazný krok vpřed v oblasti vývoje moderních webových aplikací s Angular. Vývojáři, kteří se naučí využívat tyto nové nástroje a přístupy, mohou očekávat efektivnější a bezproblémovější vývojové procesy.

Jak využít testovací háčky komponenty Video a rozsahy poskytovatelů v Angularu pro efektivní vývoj

Při vývoji aplikací v Angularu se testování stává nezbytnou součástí každého projektu. Pro složitější komponenty, jakými jsou například multimediální komponenty, je klíčové mít přehledný a funkční testovací nástroj. Testování pomocí komponentových háčků (harnesses) v Angularu nám umožňuje efektivněji kontrolovat a ověřovat chování komponent během testů. V této kapitole se zaměříme na implementaci testovacího háčku pro komponentu Video a také se podíváme na různé možnosti používání poskytovatelů v Angularu, které zjednodušují práci s injekcí závislostí a umožňují flexibilnější práci s komponentami.

Pro začátek si ukážeme, jak implementovat testovací háček pro komponentu Video, který bude k dispozici při testování z komponenty Course. Tento přístup nám poskytne možnost snadno přistupovat k metodám komponenty a ověřovat její funkčnost. Například, jak ověřit, že videoId je správně dostupné při renderování videa:

typescript
it('should have the videoId available when rendering the video', async () => {
const renderedVideos = await loader.getAllHarnesses(VideoHarness);
renderedVideos.
forEach(async (video: VideoHarness) => { const videoId = await video.getVideoId(); expect(videoId).toBeTruthy(); }) })

Důležitou součástí testování komponenty Video je správné zajištění dostupnosti metody pro získání videoId. Kompletní testovací háček pro Video komponentu může vypadat následovně:

typescript
export class VideoHarness extends ComponentHarness {
static hostSelector = 'workspace-video'; protected getTextElement = this.locatorFor('.text');
protected getVideoElement = this.locatorFor(YoutubePlayerHarness);
async getText(): Promise<string> {
const textElement = await this.getTextElement();
return textElement.text(); } async getVideoId(): Promise<string> {
const videoElement = await this.getVideoElement();
return videoElement.getVideoId(); } textEquals(video: IVideo, text: string): boolean { return text?.toLowerCase().trim().includes(video.title.trim().toLowerCase()); } }

Tento testovací háček nám umožňuje získat textové informace o videu a jeho videoId, což je užitečné pro testování logiky spojené s multimediálními prvky v aplikaci. Takto můžeme poskytnout nástroj pro testování komponenty Video, který je snadno použitelný pro kohokoli, kdo chce využít tuto komponentu ve své aplikaci.

Po tomto úvodu do testování komponenty Video přecházíme k dalšímu tématu, kterým je používání různých rozsahů poskytovatelů v Angularu pro optimalizaci správy závislostí. Pokud v aplikaci používáme službu, která by měla být k dispozici v různých kontextech nebo modulech s odlišnými konfiguracemi, můžeme využít různé poskytovatele, jako je například any provider scope. Tento rozsah poskytovatelů nám umožňuje specifikovat, že služba bude mít jinou instanci v každém modulu nebo kontextu.

Například, když pracujeme s konfigurací tématu (ThemeService), můžeme využít any rozsah poskytovatelů pro načítání specifických nastavení pro různé části aplikace. Tento přístup je zvláště užitečný při lazy loadingu modulů, kdy potřebujeme mít pro každý modul vlastní konfiguraci.

typescript
@Injectable({
providedIn: 'any', }) export class ThemeService { constructor(@Inject(themeToken) private theme: ITheme) {} public setSetting(name: string, value: string): void { this.setItem(name, value); } public getSetting(name: string): string { switch (name) { case 'background': return this.getItem(name) ?? this.theme.background; case 'tileBackground': return this.getItem(name) ?? this.theme.tileBackground; case 'headerBackground': return this.getItem(name) ?? this.theme.headerBackground; case 'textSize': return this.getItem(name) ?? this.theme.textSize; case 'videoSize': return this.getItem(name) ?? this.theme.videoSize; } return 'white'; } private setItem(name: string, value: string): void {
localStorage.setItem(this.prefix(name), value);
}
private getItem(name: string): string | null {
return localStorage.getItem(this.prefix(name));
}
private prefix(name: string): string {
return this.theme.id + '_' + name;
} }

V tomto příkladu máme ThemeService, který je nakonfigurován pomocí tokenu pro téma (themeToken). Tato služba bude poskytována v rámci any rozsahu, což znamená, že každá instance aplikace, která potřebuje vlastní konfiguraci, bude mít svou vlastní instanci služby s vlastními nastaveními. Tento přístup umožňuje velkou flexibilitu při práci s různými konfiguracemi napříč moduly.

Pokud použijeme any rozsah, můžeme takto snadno mít různá témata pro různé části aplikace. Tato flexibilita je klíčová pro správu různých aspektů aplikace, které mohou mít specifické potřeby v různých kontextech. Takto nakonfigurované služby poskytují aplikaci modularitu a snadnou údržbu, což je výhodné zejména při práci s většími aplikacemi, které mají mnoho dynamických částí.

Při použití různých rozsahů poskytovatelů, jako je any, získáváme větší kontrolu nad tím, jak jsou závislosti spravovány a jak je aplikace modularizována. Tento přístup také umožňuje efektivněji spravovat paměť a výkon aplikace, protože každý modul může mít svou vlastní specifickou instanci služby, čímž se snižuje nároky na globální instanci.