Wróć do strony głównej
Angular

Teleportacja w Angularze

Teleportację, w kontekście aplikacji Angularowej, można określić jako zmianę umiejscowienia fragmentu widoku (w szczególności przeniesienie go do innego komponentu), przy jednoczesnym zachowaniu wiązań z danymi i eventami oryginalnego komponentu. Jest to w pewien sposób analogiczne do mechanizmu content projection, jednak bez konieczności zachowania relacji rodzic-dziecko (fragment może być przeteleportowany np. z dziecka do rodzica).

Możliwość zmiany pozycji elementów w drzewie DOM, przy zachowaniu oryginalnych wiązań widoku z komponentem, jest naturalną cechą Angulara, wynikającą z istnienia (również w runtime) logicznego drzewa komponentów i ich powiązań z szablonami widoków w zupełnym oderwaniu od drzewa DOM (zmiany w drzewie DOM nie wpływają na zmiany w logicznym drzewie komponentów).

src: https://i.redd.it/kko442mrgim71.jpg

Kiedy przydaje się teleportacja?

Jak wspomniano wyżej – gdy samo content projection nie wystarczy i zmuszeni jesteśmy do miejscowego odwrócenia zależności w drzewie komponentów (fragment widoku przodka zależeć będzie od jego potomka). Przykładem może być widget osadzony w nagłówku naszej strony, zmieniający się w zależności od podstrony, na której znajduje się użytkownik.

Gotowe rozwiązania

@angular/cdk/portal

https://material.angular.io/cdk/portal/overview

Portal będący częścią The Component Dev Kit (w skrócie cdk) to niskopoziomowy mechanizm wykorzystany do stworzenia innych elementów biblioteki angular material (m.in. overlays, z którego korzystają np. dialogi). Składa się z 2 elementów:

  • portal outlet, czyli “slot” na fragment widoku, który chcemy dynamicznie wyrenderować,
  • fragment widoku, który chcemy dynamicznie wyrenderować (w postaci referencji na element DOM, templateRef lub na komponent).

Aby teleportacja zadziała się na odległość sami musimy stworzyć mechanizm, który przekaże nam fragment widoku z odpowiedniego miejsca do portal outletu. Łatwo osiągnąć to tworząc serwis (singleton), który będzie jednocześnie dokonywać rejestracji (zapamiętania) outletu oraz rozpoczynania i kończenia teleportacji.

Metoda registerPortalOutlet przyjmuje w naszym przypadku referencję na element DOM i tworzy w nim nasz “slot” na fragment widoku. Natomiastmetoda unregisterPortalOutlet niszczy taki slot. Metody teleport i finishTeleportation służą odpowiednio do rozpoczęcia i zakończenia renderowania przekazanego (jako “portal”) fragmentu widoku.

Przykładowa implementacja po stronie contentu do przeteleportowania:

Za pomocą @ViewChild tworzymy referencję na element DOM, tworzymy z niego instancję klasy DomPortal i przekazujemy do naszego serwisu. Pamiętamy o tym, by w ngOnDestroy zakończyć teleportację.

Przykładowa implementacja po stronie portal outletu:

 

Analogicznie do poprzedniego fragmentu tworzymy referencję do elementu DOM, który będzie naszym “slotem” na dynamiczny content i przekazujemy ją do serwisu. W ngOnDestroy pamiętamy o zniszczeniu slotu.

 

Powyższa implementacja przedstawia wersję uproszczoną. Nic nie stoi na przeszkodzie, by wprowadzić możliwość występowania wielu slotów i identyfikowania ich za pomocą kluczy oraz obsługi przypadku, w którym najpierw dostarczamy fragment UI do teleportacji, a potem dopiero dokonujemy rejestracji outletu. Zamiast ręcznego przekazywania referencji do serwisu można stworzyć do tego specjalne dyrektywy.

@ngneat/overview > teleporting

https://github.com/ngneat/overview#Teleporting

Biblioteka @ngneat/overview dostarcza nam pakiet dwóch dyrektyw:

  • teleportOutlet, która tworzy w danym miejscu “slot”,
  • *teleportTo, która oznacza content do teleportacji.

Fragment kodu z teleportOutlet z dokumentacji:

Fragment kodu z *teleportTo z dokumentacji:

Własna implementacja

Jeśli nie chcemy lub nie możemy skorzystać z gotowych rozwiązań to w dość prosty sposób jesteśmy w stanie stworzyć cały mechanizm od zera.

W roli “slotu” na dynamiczny content działać będzie ViewContainerRef.

Czerpiąc głęboką inspirację z rozwiązania od @ngneat tworzymy dwie dyrektywy:

Przykład użycia wygląda następująco:

Tak jak w przypadku przykładu z @angular/cdk docelowe rozwiązanie warto rozbudować o dodatkowe możliwości i zabezpieczenia.

Zakończenie

Repozytorium, zawierające wszystkie powyższe implementacje, znajduje się pod poniższym linkiem:

https://github.com/mateusz-dobrowolski-va/angular-teleportation

O autorze

Mateusz Dobrowolski

Angular Developer w House of Angular. Sympatyk Typescripta mający kilkuletnie doświadczenie w tworzeniu Angularowych aplikacji.

Zapisz się do naszego newslettera. Bądź na bieżąco z najnowszymi trendami, poradami, meetupami i stań się częścią społeczności Angulara w Polsce. Rynek pracy docenia członków społeczności.

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *