Tutto quello che dovete sapere su ng-template, ng-content, ng-container e *ngTemplateOutlet in Angular

Lug 1, 2021
admin

Era uno di quei giorni in cui ero occupato a lavorare su nuove funzionalità per il mio progetto in ufficio. All’improvviso, qualcosa ha attirato la mia attenzione:

DOM finale renderizzato in Angular

Durante l’ispezione del DOM ho visto il ngcontent applicato da Angular sugli elementi. Hmm… se contengono gli elementi nel DOM finale, allora a cosa serve il <ng-container>? In quel momento mi sono confuso tra <ng-container> e <ng-content>.

Nella ricerca delle risposte alle mie domande ho scoperto il concetto di <ng-template>. Con mia grande sorpresa, c’era anche *ngTemplateOutlet. Ho iniziato il mio viaggio cercando chiarezza su due concetti, ma ora ne avevo quattro, che suonavano quasi uguali!

Ti sei mai trovato in questa situazione? Se sì, allora siete nel posto giusto. Quindi, senza ulteriori indugi, prendiamoli uno per uno.

<ng-template>

Come suggerisce il nome, il <ng-template> è un elemento template che Angular usa con direttive strutturali (*ngIf, *ngFor, e direttive personalizzate).

Questi elementi template funzionano solo in presenza di direttive strutturali. Angular avvolge l’elemento host (a cui viene applicata la direttiva) dentro <ng-template> e consuma il <ng-template> nel DOM finito sostituendolo con commenti diagnostici.

Considerate un semplice esempio di *ngIf:

Esempio 1- Processo Angular di interpretazione delle direttive strutturali

Viene mostrata sopra l’interpretazione Angular di *ngIf. Angular mette l’elemento host a cui viene applicata la direttiva dentro <ng-template> e mantiene l’host così com’è. Il DOM finale è simile a quello che abbiamo visto all’inizio di questo articolo:

Esempio 1- DOM finale renderizzato

Uso:

Abbiamo visto come Angular usa <ng-template> ma se volessimo usarlo? Poiché questi elementi funzionano solo con una direttiva strutturale, possiamo scrivere come:

Esempio 2- Utilizzo di <ng-template>

Qui home è una proprietà boolean del componente impostato al valore true. L’output del codice di cui sopra in DOM:

Esempio 2- DOM renderizzato finale

Non è stato reso nulla! 🙁

Ma perché non possiamo vedere il nostro messaggio anche dopo aver usato <ng-template> correttamente con una direttiva strutturale?

Questo era il risultato atteso. Come abbiamo già discusso, Angular sostituisce il <ng-template> con commenti diagnostici. Senza dubbio il codice di cui sopra non genererebbe alcun errore, poiché Angular è perfettamente a posto con il vostro caso d’uso. Non riusciresti mai a sapere cosa è successo esattamente dietro le quinte.

Confrontiamo i due DOM di cui sopra che sono stati resi da Angular:

Esempio 1 vs Esempio 2

Se guardi attentamente, c’è un tag di commento extra nel DOM finale dell’Esempio 2. Il codice che Angular ha interpretato è stato:

Processo di interpretazione di Angular per l’Esempio 2

Angular ha avvolto il tuo host <ng-template> dentro un altro <ng-template> e ha convertito non solo il <ng-template> esterno in commenti diagnostici ma anche quello interno! Questo è il motivo per cui non potevi vedere nessuno dei tuoi messaggi.

Per sbarazzarti di questo ci sono due modi per ottenere il risultato desiderato:

Uso corretto di <ng-template>

Metodo 1:

In questo metodo, stai fornendo ad Angular il formato de-sugared che non ha bisogno di ulteriore elaborazione. Questa volta Angular convertirebbe solo <ng-template> in commenti, ma lascia il contenuto al suo interno intatto (non sono più dentro nessun <ng-template> come nel caso precedente). Così, renderà il contenuto correttamente.

Per saperne di più su come utilizzare questo formato con altre direttive strutturali, fate riferimento a questo articolo.

Metodo 2:

Questo è un formato abbastanza inedito e viene usato raramente (usando due <ng-template> fratelli). Qui stiamo dando un riferimento al template *ngIf nel suo then per dirgli quale template dovrebbe essere usato se la condizione è vera.

L’uso di più <ng-template> come questo non è consigliato (si potrebbe usare <ng-container> invece) perché non è quello per cui sono fatti. Sono usati come un contenitore di modelli che possono essere riutilizzati in più posti. Ne parleremo meglio in una sezione successiva di questo articolo.

<ng-container>

Hai mai scritto o visto del codice simile a questo:

Esempio 1

La ragione per cui molti di noi scrivono questo codice è l’impossibilità di usare più direttive strutturali su un singolo elemento ospite in Angular. Ora questo codice funziona bene ma introduce diversi <div> vuoti extra nel DOM se item.id è un valore falso che potrebbe non essere richiesto.

Esempio 1- DOM renderizzato finale

Potrebbe non essere preoccupato per un semplice esempio come questo, ma per un’applicazione enorme che ha un DOM complesso (per visualizzare decine di migliaia di dati) questo potrebbe diventare problematico in quanto gli elementi potrebbero avere degli ascoltatori attaccati a loro che saranno ancora lì nel DOM ad ascoltare gli eventi.

Quello che è ancora peggio è il livello di annidamento che si deve fare per applicare lo stile (CSS)!

Image from: Inside Unbounce

Nessuna preoccupazione, abbiamo <ng-container> in soccorso!

L’angolare <ng-container> è un elemento di raggruppamento che non interferisce con stili o layout perché Angular non lo mette nel DOM.

Quindi se scriviamo il nostro Esempio 1 con <ng-container>:

Esempio 1 con <ng-container>

Abbiamo il DOM finale come:

DOM finale reso con <ng-container>

Vedi che ci siamo liberati di quei <div> vuoti. Dovremmo usare <ng-container> quando vogliamo solo applicare più direttive strutturali senza introdurre alcun elemento extra nel nostro DOM.

Per maggiori informazioni fate riferimento alla documentazione. C’è un altro caso d’uso in cui viene utilizzato per iniettare dinamicamente un template in una pagina. Tratterò questo caso d’uso nell’ultima sezione di questo articolo.

<ng-content>

Sono usati per creare componenti configurabili. Questo significa che i componenti possono essere configurati a seconda delle esigenze del suo utente. Questo è ben noto come proiezione del contenuto. I componenti che sono usati nelle librerie pubblicate fanno uso di <ng-content> per rendersi configurabili.

Considera un semplice componente <project-content>:

Esempio 1- <project-content> definizione
Proiezione di contenuto con <project-content> componente

Il contenuto HTML passato dentro i tag di apertura e chiusura del componente <project-content> è il contenuto da proiettare. Questo è ciò che chiamiamo Proiezione del contenuto. Il contenuto sarà reso all’interno del <ng-content> all’interno del componente. Questo permette all’utente del componente <project-content> di passare qualsiasi piè di pagina personalizzato all’interno del componente e controllare esattamente come vogliono che sia reso.

Proiezioni multiple:

E se tu potessi decidere quale contenuto dovrebbe essere messo dove? Invece di ogni contenuto proiettato all’interno di un singolo <ng-content>, puoi anche controllare come il contenuto verrà proiettato con l’attributo select di <ng-content>. Ci vuole un selettore di elementi per decidere quale contenuto proiettare all’interno di un particolare <ng-content>.

Ecco come:

Esempio 2- Proiezione multi-contenuto con <project-content>

Abbiamo modificato la definizione <project-content> per eseguire la proiezione multi-contenuto. L’attributo select seleziona il tipo di contenuto che sarà reso all’interno di un particolare <ng-content>. Qui abbiamo prima select per rendere l’intestazione h1 dell’elemento. Se il contenuto proiettato non ha alcun elemento h1 non renderà nulla. Allo stesso modo il secondo select cerca un div. Il resto del contenuto viene reso all’interno dell’ultimo <ng-content> senza select.

Chiamare il componente sarà come:

Esempio 2- Chiamata del componente <project-content> nel componente padre

*ngTemplateOutlet

…Sono usati come un contenitore di template che possono essere riutilizzati in più posti. Ne parleremo meglio in una sezione successiva di questo articolo.
…C’è un altro caso d’uso in cui viene utilizzato per iniettare dinamicamente un template in una pagina. Tratterò questo caso d’uso nell’ultima sezione di questo articolo.

Questa è la sezione dove discuteremo i due punti menzionati prima. *ngTemplateOutlet è usato per due scenari – per inserire un template comune in varie sezioni di una vista indipendentemente dai cicli o dalle condizioni e per fare un componente altamente configurato.

Riutilizzo del template:

Considera una vista dove devi inserire un template in più punti. Per esempio, un logo aziendale da inserire all’interno di un sito web. Possiamo ottenerlo scrivendo il template per il logo una volta e riutilizzandolo ovunque all’interno della vista.

Segue lo snippet di codice:

Esempio 1- Riutilizzo del template

Come potete vedere abbiamo appena scritto il template del logo una volta e lo abbiamo usato tre volte nella stessa pagina con una sola riga di codice!

*ngTemplateOutlet accetta anche un oggetto context che può essere passato per personalizzare l’output comune del template. Per maggiori informazioni sull’oggetto contesto, fate riferimento alla documentazione ufficiale.

Componenti personalizzabili:

Il secondo caso d’uso di *ngTemplateOutlet è costituito da componenti altamente personalizzati. Considerate il nostro precedente esempio di <project-content>componente con alcune modifiche:

Esempio 2- Rendere il componente personalizzabile, project-content.html

Sopra è la versione modificata del componente <project-content> che accetta tre proprietà di input – headerTemplate, bodyTemplate, footerTemplate. Di seguito lo snippet per project-content.ts:

Esempio 2- Rendere il componente personalizzabile, project-content.ts

Quello che stiamo cercando di ottenere qui è di mostrare header, body e footer come ricevuti dal componente padre di <project-content>. Se uno qualsiasi di essi non viene fornito, il nostro componente mostrerà il modello predefinito al suo posto. Quindi, creando un componente altamente personalizzato.

Per usare il nostro componente recentemente modificato:

Esempio 2- Usando il componente appena modificato <project-content>

Ecco come passeremo i riferimenti dei template al nostro componente. Se uno qualsiasi di essi non viene passato, allora il componente renderà il template di default.

ng-content vs. *ngTemplateOutlet

Entrambi ci aiutano ad ottenere componenti altamente personalizzati, ma quale scegliere e quando?

Si può vedere chiaramente che *ngTemplateOutlet ci dà qualche potere in più nel mostrare il template di default se nessuno è fornito.

Questo non è il caso di ng-content. Rende il contenuto così com’è. Al massimo potete dividere il contenuto e renderlo in diverse posizioni della vostra vista con l’aiuto dell’attributo select. Non potete rendere condizionatamente il contenuto all’interno di ng-content. Dovete mostrare il contenuto che viene ricevuto dal genitore senza mezzi per prendere decisioni basate sul contenuto.

Tuttavia, la scelta di selezionare tra i due dipende completamente dal vostro caso d’uso. Almeno ora abbiamo una nuova arma *ngTemplateOutlet nel nostro arsenale che fornisce più controllo sul contenuto in aggiunta alle caratteristiche di ng-content!

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.