Clipboard API Angular CDK poskytuje dvě hlavní možnosti pro interakci s clipboardem operačního systému přes prohlížeč: direktivu CdkCopyToClipboard a službu Clipboard. Každý z těchto nástrojů má své specifické využití v závislosti na konkrétních požadavcích aplikace. Direktiva CdkCopyToClipboard je určena pro deklarativní použití, zatímco služba Clipboard je vhodná pro situace, kdy je potřeba programově kopírovat text nebo mít větší kontrolu nad kopírováním rozsáhlých textů.

CdkCopyToClipboard Direktiva

Tato direktiva je exportována z modulu ClipboardModule, který je součástí balíčku @angular/cdk/clipboard. Používá se pomocí selektoru [cdkCopyToClipboard] a má vstupní vlastnost stejného názvu, která přijímá text, jenž bude zkopírován do clipboardu po kliknutí na prvek, ke kterému je direktiva připojena. Z důvodu bezpečnostních omezení prohlížečů musí být operace kopírování provedena v reakci na událost kliknutí iniciovanou uživatelem.

Další vstupní vlastností je cdkCopyToClipboardAttempts, která přijímá číslo určující počet pokusů o kopírování, které se direktiva pokusí provést, než operaci vzdá. Toto je důležité zejména při kopírování větších textů, kdy některé implementační detaily zajišťují kompatibilitu napříč prohlížeči. Tento mechanismus bude diskutován podrobněji v části o třídě PendingCopy.

Direktiva rovněž disponuje výstupní vlastností cdkCopyToClipboardCopied, která vrací boolean hodnotu, jež indikuje, zda bylo kopírování úspěšné. Tento výstup je užitečný pro zpětnou vazbu uživateli nebo pro další logiku aplikace.

Služba Clipboard

Služba Clipboard poskytuje větší flexibilitu než direktiva, jelikož umožňuje provádět další operace před nebo po kopírování textu. Je zvláště užitečná v případech, kdy text není snadno dostupný v šabloně komponenty nebo pokud je třeba jemněji ovládat proces kopírování větších textů.

Služba obsahuje metody Clipboard#copy a Clipboard#beginCopy. První z nich přijímá text k zkopírování a vrací boolean hodnotu, která indikuje, zda bylo kopírování úspěšné. Při práci s velkými texty však může metoda copy selhat. V těchto případech je vhodné použít metodu beginCopy, která vrací instanci třídy PendingCopy. Tento objekt je nutné dále zpracovávat, aby bylo kopírování dokončeno.

Třída PendingCopy

Instanci třídy PendingCopy obdržíme voláním metody Clipboard#beginCopy. Tato třída je navržena tak, aby zajistila kompatibilitu s prohlížeči při kopírování větších textů. Jakmile je kopírování dokončeno nebo zrušeno, musíme zavolat metodu PendingCopy#destroy, abychom zajistili, že dojde k uvolnění zdrojů, jinak by mohlo dojít k úniku paměti.

Metoda PendingCopy#copy neakceptuje žádné argumenty a vrací boolean hodnotu indikující, zda operace kopírování byla úspěšná. Pokud je výsledek false, měli bychom naplánovat nový pokus o kopírování v pozdější fázi.

Co dalšího je důležité pochopit?

Kromě základních metod a funkcí, které Clipboard API nabízí, je důležité si uvědomit, že kopírování do clipboardu je závislé na konkrétní implementaci prohlížeče a jeho bezpečnostních opatřeních. I když většina moderních prohlížečů dnes tento proces podporuje, existují situace, kdy se může kopírování selhat, zejména při práci s velkými texty. V těchto případech je nezbytné používat metodu beginCopy a správně pracovat s třídou PendingCopy, aby byla zajištěna robustnost aplikace.

Další důležitou věcí, kterou je třeba mít na paměti, je to, že operace kopírování musí být spuštěna interakcí uživatele. To znamená, že jakékoli pokusy o automatické kopírování textů bez účasti uživatele mohou být blokovány prohlížečem. To je klíčová součást, kterou je potřeba správně implementovat v rámci uživatelského rozhraní aplikace.

Pokud pracujete s rozsáhlými texty nebo složitějšími interakcemi, služba Clipboard nabízí mnohem větší flexibilitu než samotná direktiva. Tato flexibilita se může hodit zejména v případě, kdy je potřeba provádět kopírování v rámci složitějších uživatelských workflow nebo při práci s daty, která nejsou přímo dostupná v komponentách.

Jak inspektovat kontext vloženého pohledu u strukturálních direktiv v Angularu?

Ve světě Angularu strukturální direktivy jako NgIf a NgFor nehrají pouze roli při rozhodování, zda se má určitý element zobrazit nebo opakovat, ale zároveň vytvářejí vlastní tzv. embedded views – vložené pohledy – které mají přidružený specifický kontext. Tento kontext můžeme během vývoje analyzovat díky novým runtime debugging nástrojům dostupným v Angular Ivy.

Když je v šabloně použita strukturální direktiva, Angular vytvoří vnitřní reprezentaci této části DOM, tzv. embedded view, která je navázána na datový kontext. Tento kontext definuje, s jakými daty a informacemi komponenta v daném místě šablony pracuje. Pomocí API ng.getContext(element) můžeme tento kontext inspektovat přímo z konzole prohlížeče, za předpokladu, že cílíme přímo na element, který má strukturální direktivu připojenou. Pokud bychom předali jiný element, dostaneme místo toho instanci komponenty, ke které náleží.

U direktivy NgIf dostaneme kontext NgIfContext, který má jednoduchou strukturu:

typescript
interface NgIfContext {
$implicit: boolean; ngIf: boolean; }

V tomto případě $implicit i ngIf vyjadřují stejnou hodnotu – boolean výraz, podle kterého se rozhoduje, zda se obsah vykreslí. To nám umožňuje přímo inspektovat, jaké podmínky vedly k vytvoření či odstranění elementu z DOM.

Zajímavější je však NgForOfContext, který se používá ve strukturální direktivě NgFor. Ten kromě $implicit – což je aktuální iterovaná hodnota – obsahuje i další vlastnosti:

typescript
interface NgForOfContext<T> { $implicit: T; count: number; index: number; ngForOf: T[]; even: boolean; first: boolean; last: boolean; odd: boolean; }

Při iteraci přes pole např. seznamu uživatelů získává každý li element svůj vlastní embedded view. Každý tento pohled má nejen aktuálního uživatele v $implicit, ale také přístup k dalším metainformacím jako je pozice (index), počet (count), příznaky první/poslední (first, last) nebo parita (odd, even).

Například:

javascript
const listItems = document.querySelectorAll('li'); ng.getContext(listItems[0]); // -> NgForOfContext { $implicit: "Nacho", count: 4, index: 0, even: true, first: true, last: false, odd: false } ng.getContext(listItems[1]); // -> NgForOfContext { $implicit: "Santosh", count: 4, index: 1, even: false, first: false, last: false, odd: true }

To vše poskytuje neuvěřitelně silný nástroj k analýze toho, co se v komponentě skutečně děje. Nejenže můžeme vidět hodnoty v každém kroku iterace, ale také si můžeme zkontrolovat, jak Angular přiděluje vlastnosti pohledům a jak je zajištěna správná synchronizace stavu a DOMu.

Aby bylo možné s tímto API efektivně pracovat, je nezbytné být v režimu vývoje, protože tyto debugovací funkce nejsou dostupné v produkčním buildu. To také znamená, že tyto nástroje slouží především vývojářům a testerům – nikoli samotné aplikaci v provozu.

Je důležité pochopit, že strukturální direktivy mohou být připojeny pouze po jedné na jeden element. Pokud chceme použít více než jednu, je nutné obalit obsah speciálním elementem (např. ng-container) a každou direktivu připojit na jinou úroveň. Tento detail může mít zásadní vliv na výsledný DOM a jeho ladění, jelikož nesprávné použití strukturálních direktiv může vést k neočekávaným výsledkům, které se obtížně debugují bez hlubší znalosti toho, jak funguje kontext vloženého pohledu.

Kromě těchto možností je vhodné připomenout si i další důležité nástroje z nového runtime debugging API: ng.getComponent a ng.getOwningComponent pro přímý přístup ke komponentám, ng.getListeners pro kontrolu event listenerů a ng.applyChanges pro manuální spuštění detekce změn po zásahu do stavu komponenty. Všechny tyto nástroje vytvářejí komplexní prostředí pro dynamické ladění bez nutnosti restartovat nebo znovu buildovat aplikaci.

Důležitým aspektem při ladění embedded views je i jejich životní cyklus. Vložený pohled může být kdykoliv zničen a znovu vytvořen v závislosti na podmínkách ve šabloně. To znamená, že reference na elementy nebo kontexty musí být vždy aktuální – jinak riskujeme práci s neexistujícími nebo neaktuálními daty.

Je rovněž klíčové pochopit rozdíl mezi $implicit a pojmenovanými vlastnostmi v šabloně. $implicit je hodnota, kterou komponenta nebo direktiva poskytuje jako výchozí, zatímco ostatní vlastnosti jsou explicitní a dostupné pod svými názvy. Při destrukturalizaci v šabloně lze $implicit pojmenovat dle potřeby, např. let user of users, kde user je alias pro $implicit.