V tomto článku se zaměříme na využívání nových vlastností a API, které byly představeny v Angular Ivy. Tyto novinky přinášejí zjednodušení a rozšíření možností pro vývoj moderních webových aplikací. Jednou z nejvýraznějších změn, které přinesl Angular Ivy, je vylepšení schopností Angular komponent, které byly rozšířeny o nové oficiální komponenty pro produkty Google, jako je YouTube Player a integrované Google Maps. Kromě těchto komponent byly do Angular CDK (Component Dev Kit) přidány i nové API, jako je Clipboard API a komponentní testovací „harnessy“, které usnadňují práci s testováním aplikací.

Jedním z nejzajímavějších přírůstků je oficiální Angular komponenta pro přehrávač YouTube. Tento přehrávač poskytuje způsob, jak snadno integrovat YouTube videa do aplikace s využitím výhod Angular datového vázání a programatického přístupu k YouTube Player API prostřednictvím referencí na komponenty. Tento přehrávač umožňuje nejen přehrávání videí, ale i jejich plnou integraci do aplikací, včetně sledování interakcí uživatele a přizpůsobení vzhledu a chování přehrávače.

Než začneme používat tuto komponentu, je nutné nainstalovat balíček @angular/youtube-player, což provedeme pomocí příkazu:

bash
ng add @angular/youtube-player

Poté musíme přidat YouTubePlayerModule do modulu, který bude tuto komponentu využívat. Příklad importu vypadá takto:

typescript
import { NgModule } from '@angular/core';
import { YouTubePlayerModule } from '@angular/youtube-player';
import { VideoComponent } from './video.component'; @NgModule({ declarations: [VideoComponent], exports: [VideoComponent], imports: [YouTubePlayerModule], }) export class VideoModule {}

Důležitým krokem před použitím komponenty je načtení YouTube IFrame API skriptu, který je nutný pro její fungování. Tento skript má velikost 100 KB a je možné jej načíst buď při spuštění aplikace, nebo dynamicky, až když je komponenta skutečně použita. Výhodou dynamického načítání je, že uživatel nezatíží svůj systém zbytečným načítáním skriptu, pokud ho ve skutečnosti nepoužívá.

Příklad implementace dynamického načítání skriptu:

typescript
import { DOCUMENT } from '@angular/common'; import { Component, Inject, OnDestroy, OnInit } from '@angular/core'; @Component({ selector: 'app-video', templateUrl: './video.component.html', }) export class VideoComponent implements OnDestroy, OnInit { private youtubeIframeScript: HTMLScriptElement;
constructor(@Inject(DOCUMENT) private document: Document) {}
ngOnInit(): void { this.youtubeIframeScript = this.document.createElement('script'); this.youtubeIframeScript.src = 'https://www.youtube.com/iframe_api';
this.youtubeIframeScript.async = true;
this.document.body.appendChild(this.youtubeIframeScript); } ngOnDestroy(): void { this.document.body.removeChild(this.youtubeIframeScript); } }

Tato metoda zajistí, že skript bude načten pouze jednou a opětovné načítání se uživatel vyhne díky mechanismu cachování prohlížeče.

Po správném načtení skriptu a nastavení komponenty můžeme přistoupit k její implementaci do šablony. Nejjednodušší způsob použití YouTube přehrávače je následující:

html
<youtube-player videoId="your-video-id"></youtube-player>

Tento kód zajišťuje zobrazení přehrávače a přehrání videa s daným ID. Kromě základního nastavení je však možné přizpůsobit celou řadu dalších parametrů, jako je například výška přehrávače, časový úsek videa, který se má přehrát, nebo specifické volby pro ovládání přehrávače.

API referenční vlastnosti

Pro práci s komponentou YouTube Player nabízí Angular API řadu vstupních a výstupních vlastností. Mezi vstupy, které můžete použít pro konfiguraci přehrávače, patří:

  • @Input() endSeconds: number | undefined; – definuje, kdy má video skončit, udává se v sekundách.

  • @Input() height: number | undefined; – nastavuje výšku přehrávače v pixelech (výchozí hodnota je 390 px).

  • @Input() playerVars: YT.PlayerVars | undefined; – umožňuje pokročilou konfiguraci přehrávače, například specifikaci režimu přehrávání.

Výstupní vlastnosti, které komponenta poskytuje, emitují události o uživatelských interakcích a změnách stavu videa, což umožňuje reagovat na různé akce uživatele, jako je pozastavení videa, změna hlasitosti nebo sledování pokroku přehrávání.

Nové API v Angular CDK

Kromě nových komponent přinesl Angular Ivy také vylepšení v oblasti testování a interakce s operačním systémem prostřednictvím API jako je Clipboard API a komponentní testovací „harnessy“. Clipboard API poskytuje jednoduché rozhraní pro interakci s clipboardem operačního systému, což znamená, že vývojáři mohou snadno implementovat funkce kopírování a vkládání do svých aplikací. Na druhé straně komponentní testovací „harnessy“ usnadňují testování komponent a jejich interakcí v rámci aplikace, čímž zjednodušují psaní jednotkových testů a end-to-end testů. Testování aplikace s využitím těchto nástrojů je efektivní a přímočaře simuluje chování uživatele.

Co je třeba vzít v úvahu

Při implementaci těchto nových vlastností a komponent je třeba brát v úvahu, že ačkoliv Angular Ivy výrazně zjednodušuje některé procesy, stále je nutné pečlivě spravovat závislosti a jejich životní cyklus. Například při práci s komponentami, které závisí na externích API, je důležité zajistit, že skripty a knihovny budou načítány efektivně a nebudou způsobovat zbytečné zátěže pro koncové uživatele. Testování je nezbytné pro zajištění, že nové funkce fungují spolehlivě, zejména v různých prostředích a s různými verzemi prohlížečů. U komponent s externími závislostmi, jako je YouTube Player, je kladeno důraz na správné nastavení a inicializaci API pro dosažení optimálního výkonu aplikace.

Jak lze ovládat CSS Grid pomocí vlastních CSS proměnných v Angularu?

V moderních webových aplikacích, kde rozvržení a přizpůsobitelnost hrají klíčovou roli, nabývá na důležitosti schopnost ovlivňovat rozložení komponenty v reálném čase. Angular poskytuje dostatečně flexibilní nástroje k propojení logiky s designem, přičemž elegantním řešením pro přizpůsobení rozvržení je použití vlastních CSS proměnných v kombinaci s CSS Gridem a media queries.

Základní architektura této techniky spočívá v definici kontejneru s dvanáctisloupcovým gridem. Uvnitř tohoto kontejneru pak jednotlivé prvky – například video dlaždice nebo textové bloky – určují svůj rozsah pomocí vlastních CSS proměnných. Konkrétně se používají dvě třídy: .video a .text, které využívají proměnné --videosize a --textsize pro určení počtu obsazených sloupců. Základní stylové pravidlo vypadá následovně:

css
.tile {
background: var(--tilebackground, grey); padding: 15px 15px 15px; overflow: hidden; &.video { grid-column: span var(--videosize, 9); } &.text { grid-column: span var(--textsize, 3); } }

Tato flexibilita přináší i další výhodu – jednoduché začlenění přístupnosti pro mobilní zařízení. Přesněji řečeno, kombinací media queries a výchozích hodnot proměnných lze bez přímé závislosti na Angularovém kódu vytvořit responzivní design, který bude fungovat i mimo rámec komponent. Například:

css
@media screen and (min-width: 768px) {
.tile.video { grid-column: span var(--videosize, 9); } .tile.text { grid-column: span var(--textsize, 3); } } @media only screen and (max-width: 768px) { .tile { grid-column: span 12; } }

Tato metoda umožňuje designérům pracovat na stylování bez nutnosti porozumění Angularovým strukturám nebo logice komponent. Významným přínosem je možnost napojení na externí systém designových tokenů, například v rámci korporátních stylů, což eliminuje potřebu redeploy aplikace při změně vizuální identity.

Na úrovni interaktivity se velikost video nebo textových dlaždic může upravovat přes Material Slider komponenty. Proměnné --videosize a --textsize jsou svázány s Angularovou službou pro správu témat, a jejich aktualizace probíhá pomocí callbacku:

ts
setSize(name: string, event: MatSliderChange): void { this.themeService.setSetting(name, event.value?.toString() || '1'); location.reload(); }

Hodnoty proměnných jsou omezeny rozsahem dostupných sloupců, přičemž běžně se doporučuje držet --videosize v rozmezí 3 až 7, čímž zůstává prostor pro odpovídající velikost textové části. Suma obou hodnot by pak měla být rovna celkovému počtu sloupců v gridu, tedy obvykle 12. Tento přístup usnadňuje udržení konzistence v rozvržení.

Používání vlastních CSS proměnných přináší silnou možnost dynamického řízení stylu přímo z tématické vrstvy aplikace. V Angularu tak můžeme skrze tematický servis měnit proměnné v rámci DOM, aniž bychom museli manipulovat se šablonami nebo komponentami. Prvky na stránce se tak stávají skutečně reaktivními z pohledu vzhledu.

Je však důležité si uvědomit, že použití komponent jako je YouTube Player, které mají vlastní vnitřní layout, může ovlivnit výsledné rozvržení. V takových případech je nutné buď přizpůsobit grid, nebo počítat s tím, že komponenta bude potřebovat specifické úpravy.

V tomto kontextu přináší spojení CSS Custom Properties a Angularu nový model spolupráce mezi vývojáři a designéry. Zatímco vývojář nastavuje logiku proměnných a jejich integraci do komponent, designér definuje vizuální pravidla bez zásahu do logiky. Tím se zásadně snižuje riziko nekompatibility a zvyšuje se udržitelnost kódu.

Důležitým aspektem je i možnost napojení těchto proměnných na vnější systémy. Firemní prostředí často využívají centrální systém design tokenů. Pokud je tento systém propojen s aplikací, můžeme zajistit, že jakákoli změna stylu se okamžitě projeví v celé aplikaci – bez nutnosti zásahu do samotné aplikace.

Takto vytvořená architektura představuje čisté oddělení vzhledu a logiky, vysokou míru přizpůsobitelnosti a snadnou rozšiřitelnost. Pro moderní webové aplikace, které kombinují škálovatelný frontend s dynamickými komponentami, jde o přístup, který nabízí rovnováhu mezi robustností a flexibilitou.

Jak zobrazit video kurzy pomocí Angular YouTube Player

V tomto oddíle se zaměříme na tvorbu samostatné komponenty pro zobrazení videí připojených ke kurzu. Pro jednoduchost přijmeme, že informace o videu budou poskytovány pomocí dekorátoru @Input ve Video komponentě. Kód komponenty může vypadat takto:

typescript
@Component({ selector: 'workspace-video', templateUrl: './video.component.html', styleUrls: ['./video.scss'], }) export class VideoComponent implements OnDestroy, OnInit { private youtubeIframeScript: HTMLScriptElement; @Input() public title!: string; @Input() public name!: string; @Input() public videoId!: string; @Input() public description!: string; @Input() public snippet!: string; get youtubeLink() { return this.title + ": https://www.youtube.com/watch?v=" + this.videoId; }
constructor(@Inject(DOCUMENT) private document: Document) {
this.youtubeIframeScript = this.document.createElement('script'); this.youtubeIframeScript.src = 'https://www.youtube.com/iframe_api';
this.youtubeIframeScript.async = true;
}
ngOnInit(): void { this.document.body.appendChild(this.youtubeIframeScript); } ngOnDestroy(): void { this.document.body.removeChild(this.youtubeIframeScript); } }

Tento kód by vám měl být známý z úvodu v kapitole 4, kde jsme se zabývali základními vlastnostmi Angular komponent. Tato komponenta nám umožňuje připojit video z YouTube k určitému kurzu. Pro zobrazení videa využíváme třídu VideoComponent, která pomocí @Input přijímá parametry jako název videa, ID videa, popis a snippet.

Pro zobrazení videa v šabloně komponenty použijeme kód následujícího typu:

html
<iframe width="100%" height="auto" [src]="youtubeLink | safeUrl" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

Díky této šabloně se dynamicky načítá YouTube video, jehož odkaz je tvořen z ID videa a dalších atributů. Zajímavým vylepšením je použití vlastnosti cdkCopyToClipboard, která umožňuje uživatelům snadno zkopírovat odkaz na video do schránky. Tento prvek je užitečný zejména v desktopových aplikacích, kde mohou uživatelé chtít rychle získat odkaz na obsah.

Pokud chcete komponentu VideoComponent použít v rámci větší aplikace, například v komponentě kurzu, stačí jednoduše implementovat následující kód:

html
<workspace-video *ngIf="course" [title]="course.title" [name]="course.name" [videoId]="course.videoId" [description]="course.description" [snippet]="course.snippet"></workspace-video>

Tento příklad využívá asynchronní operátor async pro získání dat o kurzu z proměnné course$, což nám umožňuje počkat na načtení dat a poté dynamicky renderovat video pomocí VideoComponent.

Důležitým prvkem je i správné propojení s komponentou kurzu, která obsahuje informace o kurzu a také ID pro připojení k jednotlivým videím. Také nezapomeňte na správnou manipulaci s dynamickým velikostním nastavením pomocí CSS, jak jsme se již zmínili v předchozích kapitolách.

Po dokončení práce s videem přejdeme k implementaci mapy pro zobrazení škol a kurzů. Ve škole může uživatel kliknout na mapu, což otevře okno s informacemi o kurzu, který si následně může prohlédnout. Tento proces je realizován pomocí Google Maps komponenty v Angularu.

Komponenta škol se bude chovat takto:

typescript
@Component({
selector: 'workspace-schools', templateUrl: './schools.component.html', styleUrls: ['./schools.component.scss'], }) export class SchoolsComponent { @ViewChild(GoogleMap, { static: false }) map!: GoogleMap;
@ViewChild(MapInfoWindow, { static: false }) info!: MapInfoWindow;
school!:
ISchool; schools$: Observable<ISchool[]>; constructor(schoolsService: SchoolsService) { this.schools$ = schoolsService.getSchools(); } openInfo(anchor: MapAnchorPoint, school: ISchool): void { this.school = school; this.info.open(anchor); } }

Tento kód ukazuje, jak otevřít informační okno s podrobnostmi o škole a dostupných kurzech při kliknutí na konkrétní bod na mapě. Také využíváme async operátor pro asynchronní načítání škol, což umožňuje efektivní zobrazení dat, jakmile jsou k dispozici.

Zobrazení informací o kurzu v tomto okně je důležité pro plynulé přechody mezi jednotlivými stránkami aplikace. Uživatelé mohou snadno přejít z mapy do detailů kurzu, což zjednodušuje navigaci a celkový uživatelský zážitek.

Pro reálnější aplikace by bylo vhodné přidat možnost přihlášení uživatele, který by si mohl ukládat své oblíbené kurzy do relace. To by znamenalo pokročilejší správu uživatelských dat, což si vysvětlíme v následujících kapitolách. Také je důležité nezapomínat na testování komponent a správu jejich závislostí pro zajištění kvalitní a bezchybné funkčnosti aplikace.

Jak efektivně využívat komponentní testovací nosiče v Angularu

Testování uživatelského rozhraní (UI) v Angularu může být výzvou, především pokud jde o těsně spjaté testy struktury DOM. S příchodem Angular Ivy však máme k dispozici novou možnost, jak tento problém řešit. Komponentní testovací nosiče (component testing harnesses) umožňují vytvářet testovací API pro naše komponenty pomocí osvědčeného vzoru Page Object, ale na mnohem podrobnější úrovni. Tato technika má výhodu v tom, že eliminuje složité manipulace s DOM, čímž zvyšuje stabilitu testů a snižuje jejich křehkost v případě změn v DOM struktuře. Navíc komponentní nosiče pro Angular Material komponenty jsou již součástí Angular Ivy, což nám usnadňuje testování těchto komponent.

V této kapitole se podíváme na to, jak využívat komponentní nosiče ve spojení s Angular komponentami a jak implementovat vlastní komponentní nosiče pro usnadnění testování našich komponent. V rámci tohoto procesu se seznámíme s testováním komponent s využitím Angular Material testovacích nosičů, ale také si ukážeme, jak si můžeme napsat vlastní testovací nosiče pro naše vlastní komponenty.

Testování s využitím Angular Material komponentních nosičů je jednoduché a efektivní. Například, pokud bychom chtěli testovat komponentu pro výběr barvy pozadí, můžeme využít MatInputHarness pro simulaci interakce uživatele. V testu očekáváme, že počáteční hodnota pro barvu pozadí bude #00aa00. Pomocí metody getValue() můžeme tuto hodnotu ověřit, jak ukazuje následující testovací scénář:

typescript
it('should be able to read default header background color theme setting', async () => {
const headerBackground: MatInputHarness = await loader.getHarness(MatInputHarness.with({ selector: '#headerBackground' }));
expect(await headerBackground.getValue()).toBe('#00aa00');
});

Pokud bychom chtěli otestovat změnu hodnoty, například změnu barvy pozadí na #ffbbcc, můžeme použít metodu setValue(), jak ukazuje následující test:

typescript
it('should be able to change the header background color theme setting', async () => {
const headerBackground: MatInputHarness = await loader.getHarness(MatInputHarness.with({ selector: '#headerBackground' }));
headerBackground.
setValue('#ffbbcc').then(() => {
expect(themeService.getSetting('headerBackground')).toBe('#ffbbcc');
}); });

Tento test využívá komponentní nosič k interakci s komponentou vstupu pro barvu pozadí, čímž eliminuje nutnost psát složité manipulace s DOM nebo volat fixture.detectChanges(), jak tomu bývalo v dřívějších testech. Testování přes komponentní nosiče je nejen efektivní, ale také zjednodušuje kód testu a činí jej stabilnějším vůči změnám v implementaci.

Pokud testujeme jiný prvek, například posuvník pro velikost videa, můžeme použít MatSliderHarness stejně jako v předchozím případě:

typescript
it('should be able to check default text and video slider settings', async () => {
const videoSizeSetting = Number(themeService.getSetting('videoSize')); expect(videoSizeSetting).toBe(7);
const videoSizeSlider: MatSliderHarness = await loader.getHarness(MatSliderHarness.with({ selector: '#videoSizeSlider' }));
expect(await videoSizeSlider.getId()).toBe('videoSizeSlider');
expect(await videoSizeSlider.getValue()).toBe(Number(videoSizeSetting));
});

Tento test ověřuje výchozí hodnoty pro nastavení velikosti videa, a to vše opět bez nutnosti přímé manipulace s DOM.

Pokud se rozhodneme vytvářet vlastní komponentní nosiče, například pro komponentu pro zobrazení videa, musíme se zaměřit na to, jak správně strukturovat naše testy a jakým způsobem skrýt detaily implementace komponenty. Představme si, že máme komponentu pro zobrazení YouTube videí, která je součástí širší komponenty pro kurz. V takovém případě může být obtížné otestovat tuto komponentu pomocí běžného přístupu "testuj jako uživatel" přímo v DOM. Místo toho je lepší použít vícevrstvý přístup, kde začneme testování v komponentě kurzu a prostřednictvím nosiče pro video (VideoHarness) budeme interagovat s vnořenou komponentou pro YouTube Player.

Následující kód ukazuje, jak můžeme napsat vlastní testovací nosič pro komponentu videa, který bude využívat Page Object vzor pro skrývání detailů implementace:

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

Tento nosič skrývá DOM operace spojené se zobrazením videa a umožňuje nám testovat pouze logiku komponenty, aniž bychom museli zasahovat do specifických implementačních detailů. Podobně můžeme napsat test pro zobrazení titulu videa, jak je znázorněno v následujícím testu:

typescript
it('should render the video title in text when displaying it', async () => {
const renderedVideos = await loader.getAllHarnesses(VideoHarness);
courseService.
getCourse('1').subscribe((course) => {
renderedVideos.forEach(async (video: VideoHarness) => {
const text = await video.getText() || ""; expect(course.videos.find((v) => video.textEquals(v, text))).toBeTruthy(); }); }); });

Tímto způsobem můžeme testovat, zda každý video titul, který je zobrazen v komponentě kurzu, odpovídá titulům videí uloženým v datové struktuře kurzu, bez nutnosti přímé manipulace s DOM.

Vytváření a používání komponentních nosičů v Angularu nejen zjednodušuje testování, ale i zlepšuje jeho údržbu a flexibilitu. Testy se stávají odolné vůči změnám v implementaci, což zvyšuje jejich stabilitu a spolehlivost.