Tutto ciò che devi sapere su ng-template, ng-content, ng-container e * ngTemplateOutlet in Angular

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

Durante l'ispezione del DOM ho visto l' ngcontentapplicazione su elementi da Angular. Hmm ... se contengono gli elementi nel DOM finale, a cosa servono ? A quel tempo mi sono confuso tra e .

Nella ricerca per conoscere le risposte alle mie domande ho scoperto il concetto di . Con mia grande sorpresa, c'era anche *ngTemplateOutlet. Ho iniziato il mio viaggio cercando chiarezza su due concetti, ma ora ne avevo quattro, quasi uguali!

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

1.

Come suggerisce il nome l' è un elemento di modello che utilizzi angolari con le direttive strutturali ( *ngIf, *ngFor, [ngSwitch]e le direttive personalizzati).

Questi elementi del modello funzionano solo in presenza di direttive strutturali . Angular avvolge l'elemento host (a cui viene applicata la direttiva) all'internoe consuma ilDOM finito sostituendolo con commenti diagnostici.

Considera un semplice esempio di *ngIf:

Sopra è mostrata l'interpretazione angolare di *ngIf. Angular inserisce l'elemento host a cui viene applicata la direttiva e mantiene l'host così com'è. Il DOM finale è simile a quello che abbiamo visto all'inizio di questo articolo:

Utilizzo:

Abbiamo visto come utilizza Angular, ma cosa succede se vogliamo usarlo? Poiché questi elementi funzionano solo con una direttiva strutturale, possiamo scrivere come:

Ecco homeuna booleanproprietà del componente impostata su truevalue. L'output del codice sopra in DOM:

Niente è stato reso! :(

Ma perché non riusciamo a vedere il nostro messaggio anche dopo averlo utilizzato correttamente con una direttiva strutturale?

Questo era il risultato atteso. Come abbiamo già discusso, Angular sostituisce con commenti diagnostici. Senza dubbio il codice sopra non genererebbe alcun errore, poiché Angular va perfettamente bene con il tuo caso d'uso. Non sapresti mai cosa è successo esattamente dietro le quinte.

Confrontiamo i due DOM precedenti che sono stati resi da Angular:

Se guardi da vicino, c'è un tag di commento aggiuntivo nel DOM finale dell'Esempio 2 . Il codice che Angular ha interpretato è stato:

Angular ha avvolto il tuo host in un altro e ha convertito non solo l'esterno in commenti diagnostici, ma anche quello interno! Questo è il motivo per cui non hai potuto vedere nessuno dei tuoi messaggi.

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

Metodo 1:

In questo metodo, fornisci ad Angular il formato de-zuccherato che non necessita di ulteriori elaborazioni. Questa volta Angular si convertirà solo in commenti ma lascerà intatto il contenuto al suo interno (non sono più all'interno di nessuno come nel caso precedente). Pertanto, renderà il contenuto correttamente.

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

Metodo 2:

Questo è un formato abbastanza invisibile ed è usato raramente (usando due fratelli ). Qui stiamo dando un riferimento al modello *ngIfnel suo thenper dirgli quale modello dovrebbe essere usato se la condizione è vera.

L'uso di multipli come questo non è consigliato (potresti usare invece) poiché questo non è ciò per cui sono destinati. Vengono utilizzati come contenitori di modelli che possono essere riutilizzati in più posizioni. Tratteremo di più su questo in una sezione successiva di questo articolo.

2.

Hai mai scritto o visto codice simile a questo:

Il motivo per cui molti di noi scrivono questo codice è l'impossibilità di utilizzare più direttive strutturali su un singolo elemento host in Angular. Ora questo codice funziona bene ma introduce diversi vuoti extra nel DOM se item.idè un valore falso che potrebbe non essere richiesto.

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

Quel che è ancora peggio è il livello di annidamento che devi fare per applicare il tuo stile (CSS)!

Nessun problema, dobbiamo in soccorso!

L'Angular è un elemento di raggruppamento che non interferisce con gli stili o il layout perché Angular non lo inserisce nel DOM .

Quindi, se scriviamo il nostro esempio 1 con :

Otteniamo il DOM finale come:

Vedi, ci siamo sbarazzati di quei vuoti . Dovremmo usare quando vogliamo solo applicare più direttive strutturali senza introdurre alcun elemento aggiuntivo nel nostro DOM.

Per ulteriori informazioni fare riferimento alla documentazione. C'è un altro caso d'uso in cui viene utilizzato per iniettare dinamicamente un modello in una pagina. Tratterò questo caso d'uso nell'ultima sezione di questo articolo.

3.

Sono utilizzati per creare componenti configurabili. Ciò significa che i componenti possono essere configurati a seconda delle esigenze dell'utente. Questo è ben noto come Content Projection . I componenti che vengono utilizzati nelle librerie pubblicate fanno uso di per rendersi configurabili.

Considera un semplice componente:

Il contenuto HTML passato all'interno dei tag di apertura e chiusura del componente è il contenuto da proiettare. Questo è ciò che chiamiamo Content Projection . Il contenuto verrà renderizzato all'interno del componente. Ciò consente al consumatore del componente di passare qualsiasi piè di pagina personalizzato all'interno del componente e controllare esattamente come desidera che venga visualizzato.

Proiezioni multiple:

E se potessi decidere quale contenuto collocare e dove? Invece di ogni contenuto proiettato all'interno di un singolo , puoi anche controllare come i contenuti verranno proiettati con l' selectattributo di . Ci vuole un selettore di elementi per decidere quale contenuto proiettare all'interno di un particolare .

Ecco come:

Abbiamo modificato la definizione per eseguire la proiezione multi-contenuto. L' selectattributo seleziona il tipo di contenuto che verrà reso all'interno di un particolare . Qui dobbiamo prima selectrendere l' h1elemento di intestazione . Se il contenuto proiettato non ha alcun h1elemento, non renderà nulla. Allo stesso modo il secondo selectcerca un file div. Il resto del contenuto viene reso all'interno dell'ultimo con no select.

La chiamata del componente sarà simile a:

4. * ngTemplateOutlet

… Sono usati come contenitori di modelli che possono essere riutilizzati in più posizioni. Tratteremo di più su questo in una sezione successiva di questo articolo.

… C'è un altro caso d'uso in cui viene utilizzato per iniettare dinamicamente un modello in una pagina. Tratterò questo caso d'uso nell'ultima sezione di questo articolo.

Questa è la sezione in cui discuteremo i due punti sopra menzionati prima. *ngTemplateOutletviene utilizzato per due scenari: inserire un modello comune in varie sezioni di una vista indipendentemente dai loop o dalle condizioni e per creare un componente altamente configurato.

Riutilizzo dei modelli:

Considera una vista in cui devi inserire un modello in più punti. Ad esempio, un logo aziendale da inserire all'interno di un sito web. Possiamo ottenerlo scrivendo una volta il modello per il logo e riutilizzandolo ovunque all'interno della vista.

Di seguito è riportato lo snippet di codice:

Come puoi vedere, abbiamo appena scritto il modello del logo una volta e lo abbiamo utilizzato tre volte sulla stessa pagina con una singola riga di codice!

*ngTemplateOutletaccetta anche un oggetto contesto che può essere passato per personalizzare l'output del modello comune. Per ulteriori informazioni sull'oggetto contesto, fare riferimento ai documenti ufficiali.

Componenti personalizzabili:

Il secondo caso d'uso per i *ngTemplateOutletcomponenti altamente personalizzati. Considera il nostro precedente esempio di componente con alcune modifiche:

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

Quello che stiamo cercando di ottenere qui è mostrare l'intestazione, il corpo e il piè di pagina ricevuti dal componente principale di . Se uno di loro non viene fornito, il nostro componente mostrerà il modello predefinito al suo posto. Quindi, creando un componente altamente personalizzato.

Per utilizzare il nostro componente modificato di recente:

Questo è il modo in cui passeremo i riferimenti del modello al nostro componente. Se uno di questi non viene passato, il componente eseguirà il rendering del modello predefinito.

ng-content rispetto a * ngTemplateOutlet

Entrambi ci aiutano a realizzare componenti altamente personalizzati ma quale scegliere e quando?

Si può chiaramente vedere che *ngTemplateOutletci dà un po 'più di potere di mostrare il modello predefinito se non ne viene fornito nessuno.

Questo non è il caso di ng-content. Rende il contenuto così com'è. Al massimo puoi dividere il contenuto e renderlo in diverse posizioni della tua vista con l'aiuto selectdell'attributo. Non è possibile eseguire il rendering condizionale del contenuto all'interno ng-content. Devi mostrare il contenuto che viene ricevuto dal genitore senza mezzi per prendere decisioni in base al contenuto.

Tuttavia, la scelta di selezionare tra i due dipende completamente dal tuo caso d'uso. Almeno ora abbiamo una nuova arma *ngTemplateOutletnel nostro arsenale che fornisce un maggiore controllo sul contenuto oltre alle funzionalità di ng-content!