Angular - ngTemplateOutlet - directiva de @angular/commons
ngTemplateOutlet - directiva de @angular/commons
Usos
La directiva NgTemplateOutlet de Angular es una herramienta poderosa que te permite crear contenido dinámico y reutilizable en tus aplicaciones. Se utiliza principalmente para insertar una plantilla (template) dentro de la vista, y puede recibir información adicional mediante el paso de contexto. Aquí tienes una lista de algunas cosas que puedes hacer con NgTemplateOutlet:
- Crear contenido reutilizable: Puedes utilizar NgTemplateOutlet para definir fragmentos de código HTML que se pueden reutilizar en varias partes de tu aplicación, lo que facilita la gestión y el mantenimiento del código.
- Generar contenido dinámico: NgTemplateOutlet permite insertar una plantilla en diferentes partes de la aplicación en función de ciertas condiciones o eventos, lo que te permite adaptar el contenido mostrado a las necesidades específicas del usuario o del contexto.
- Personalizar componentes: Puedes utilizar NgTemplateOutlet para proporcionar plantillas personalizadas a un componente, lo que te permite personalizar su apariencia y comportamiento sin tener que modificar el código del componente en sí.
- Crear listas y tablas dinámicas: Puedes utilizar NgTemplateOutlet junto con otras directivas, como *ngFor, para crear listas o tablas que muestren contenido dinámico en función de los datos proporcionados.
- Implementar patrones de diseño: NgTemplateOutlet puede ser útil para implementar patrones de diseño como el patrón de transclusión de contenido, que permite proyectar contenido de un componente a otro.
- Pasar el contexto: Con la ayuda de la directiva NgTemplateOutlet, puedes pasar el contexto que incluye datos adicionales a la plantilla, lo que te permite utilizar esos datos para personalizar la apariencia o el comportamiento del contenido generado.
- Crear vistas anidadas: Puedes utilizar NgTemplateOutlet para crear vistas anidadas, lo que facilita la organización y la estructuración de aplicaciones más grandes y complejas.
- Mejorar la performance: Al utilizar NgTemplateOutlet para generar contenido dinámico solo cuando es necesario, puedes mejorar el rendimiento de tu aplicación al reducir la cantidad de elementos del DOM y los enlaces de datos que deben gestionarse en tiempo de ejecución.
Para utilizar NgTemplateOutlet en tu aplicación Angular, simplemente debes agregar la directiva ngTemplateOutlet al elemento del DOM donde deseas insertar la plantilla, y proporcionar la referencia a la plantilla que quieres utilizar.
Ejemplo básico - contenido reutilizable
- Primero, crea un componente Angular y define una plantilla utilizando la etiqueta
<ng-template>
:
<!-- app.component.html -->
<div>
<ng-container *ngTemplateOutlet="miTemplate; context: miContexto"></ng-container>
</div>
<ng-template #miTemplate let-mensaje="mensaje">
<p></p>
</ng-template>
- Luego, en el archivo TypeScript del componente, define el contexto que deseas pasar a la plantilla:
// app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
})
export class AppComponent {
miContexto = {
mensaje: '¡Hola, mundo!',
};
}
En este ejemplo, hemos creado una plantilla llamada miTemplate
que muestra un mensaje. La directiva *ngTemplateOutlet
se utiliza para insertar la plantilla miTemplate
dentro de un contenedor <ng-container>
. Además, hemos pasado el contexto miContexto
a la plantilla, que contiene un mensaje que se muestra en el párrafo <p>
.
Este ejemplo es bastante simple, pero puedes ampliarlo y combinarlo con otras características y directivas de Angular para crear contenido más dinámico y reutilizable.
Ejemplo - contenido dinámico
En este ejemplo, vamos a crear una lista de usuarios con información básica y un botón para mostrar más detalles de cada usuario. Cuando se haga clic en el botón, se mostrará una tarjeta con la información adicional del usuario.
- Primero, crea el componente Angular y define las plantillas en el archivo HTML:
<!-- user-list.component.html -->
<div *ngFor="let user of users">
<h3></h3>
<p></p>
<button (click)="selectedUser = selectedUser === user ? null : user">Mostrar detalles</button>
<ng-container *ngTemplateOutlet="userDetails; context: { user: selectedUser === user ? user : null }"></ng-container>
</div>
<ng-template #userDetails let-user="user">
<ng-container *ngIf="user">
<div class="user-details">
<p>Dirección: </p>
<p>Teléfono: </p>
</div>
</ng-container>
</ng-template>
- Luego, en el archivo TypeScript del componente, define los datos de los usuarios y la variable
selectedUser
:
// user-list.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-user-list',
templateUrl: './user-list.component.html',
styleUrls: ['./user-list.component.scss'],
})
export class UserListComponent {
users = [
{ name: 'Juan Pérez', email: 'juan@example.com', address: 'Calle 123', phone: '555-1234' },
{ name: 'María Rodríguez', email: 'maria@example.com', address: 'Calle 456', phone: '555-5678' },
// ...
];
selectedUser = null;
}
En este ejemplo, hemos creado un componente llamado UserListComponent
que muestra una lista de usuarios y utiliza la directiva *ngFor
para iterar sobre el arreglo users
. Para cada usuario, se muestra su nombre, correo electrónico y un botón para mostrar más detalles.
Cuando se hace clic en el botón "Mostrar detalles", la variable selectedUser
se actualiza con el usuario seleccionado. La directiva *ngTemplateOutlet
se utiliza para insertar la plantilla userDetails
en la vista y pasar el contexto que contiene el usuario seleccionado.
La plantilla userDetails
muestra la información adicional del usuario (dirección y teléfono) solo si el usuario seleccionado coincide con el usuario actual de la iteración. De lo contrario, no se muestra nada.
Ejemplo - personalizando un componente
- Primero, crea el componente de tarjeta genérica y define la plantilla en el archivo HTML:
<!-- card.component.html -->
<div class="card">
<h3 class="card-title"></h3>
<div class="card-content">
<ng-container *ngTemplateOutlet="contentTemplate"></ng-container>
</div>
</div>
- Luego, en el archivo TypeScript del componente, define las entradas para el título y la plantilla de contenido:
// card.component.ts
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-card',
templateUrl: './card.component.html',
styleUrls: ['./card.component.scss'],
})
export class CardComponent {
@Input() title: string;
@Input() contentTemplate: TemplateRef<any>;
}
- Ahora, en otro componente (por ejemplo, AppComponent), utiliza el componente de tarjeta y proporciona una plantilla personalizada para cambiar su apariencia y comportamiento:
<!-- app.component.html -->
<app-card [title]="'Tarjeta personalizada'" [contentTemplate]="customCardTemplate"></app-card>
<ng-template #customCardTemplate>
<p>Este es el contenido personalizado de la tarjeta.</p>
<button (click)="onButtonClick()">Presiona aquí</button>
</ng-template>
- En el archivo TypeScript del componente que utiliza la tarjeta, define el comportamiento personalizado (en este caso, un método para manejar el clic del botón):
// app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
})
export class AppComponent {
onButtonClick() {
alert('Botón personalizado presionado!');
}
}
En este ejemplo, hemos creado un componente genérico de tarjeta llamado CardComponent
que acepta un título y una plantilla de contenido como entradas. La plantilla de contenido se inserta en la tarjeta utilizando la directiva *ngTemplateOutlet
.
El componente AppComponent
utiliza el componente de tarjeta y proporciona una plantilla personalizada llamada customCardTemplate
para cambiar la apariencia y el comportamiento de la tarjeta. La plantilla personalizada incluye un párrafo y un botón con un evento de clic que llama al método onButtonClick
en el componente AppComponent
.
Ejemplo - Crear tabla dinámica con contenido dinámico
- Primero, crea el componente y define las plantillas en el archivo HTML:
<!-- product-table.component.html -->
<table>
<thead>
<tr>
<th>#</th>
<th>Nombre del producto</th>
<th>Precio</th>
</tr>
</thead>
<tbody>
<ng-container *ngFor="let product of products; let i = index">
<ng-container *ngTemplateOutlet="rowTemplate; context: { $implicit: product, index: i }"></ng-container>
</ng-container>
</tbody>
</table>
<ng-template #rowTemplate let-product let-i="index">
<tr [class.highlight]="i % 2 === 0">
<td></td>
<td></td>
<td></td>
</tr>
</ng-template>
- Luego, en el archivo TypeScript del componente, define los datos de los productos:
// product-table.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-product-table',
templateUrl: './product-table.component.html',
styleUrls: ['./product-table.component.scss'],
})
export class ProductTableComponent {
products = [
{ name: 'Producto A', price: 100 },
{ name: 'Producto B', price: 200 },
{ name: 'Producto C', price: 150 },
// ...
];
}
En este ejemplo, hemos creado un componente llamado ProductTableComponent
que muestra una tabla con información sobre productos y sus precios. Utilizamos la directiva *ngFor para iterar sobre el arreglo products
y crear una fila para cada producto en la tabla.
La directiva *ngTemplateOutlet se utiliza junto con *ngFor para insertar la plantilla rowTemplate
en la tabla para cada producto. Pasamos el contexto que contiene el producto actual y su índice a la plantilla.
En la plantilla rowTemplate
, utilizamos las variables product
e i
(índice) para personalizar el contenido de cada fila. Aplicamos una clase CSS highlight
a las filas pares (índice par) para resaltarlas. También mostramos el número de fila (índice + 1), el nombre del producto y su precio en formato de moneda.
Ejemplo - Trasclusión de contenido
- Primero, crea el componente de tarjeta y define la plantilla en el archivo HTML:
<!-- card.component.html -->
<div class="card" [ngStyle]="{'background-color': backgroundColor}">
<h2></h2>
<ng-container *ngTemplateOutlet="contentTemplate"></ng-container>
</div>
- Luego, en el archivo TypeScript del componente de la tarjeta, define las entradas para el título, el color de fondo y la plantilla de contenido:
// card.component.ts
import { Component, Input, TemplateRef } from '@angular/core';
@Component({
selector: 'app-card',
templateUrl: './card.component.html',
styleUrls: ['./card.component.scss'],
})
export class CardComponent {
@Input() title: string;
@Input() backgroundColor: string;
@Input() contentTemplate: TemplateRef<any>;
}
- A continuación, crea el componente principal (AppComponent) y define la plantilla en el archivo HTML:
<!-- app.component.html -->
<div style="display: flex">
<ng-template #content1>
<p style="color: red;">Este es el contenido personalizado con letras rojas.</p>
</ng-template>
<ng-template #content2>
<p style="color: blue;">Este es el contenido personalizado con letras azules.</p>
</ng-template>
<app-card title="Tarjeta 1" backgroundColor="lightblue" [contentTemplate]="content1"></app-card>
<app-card title="Tarjeta 2" backgroundColor="lightgreen" [contentTemplate]="content2"></app-card>
</div>
- En el archivo TypeScript del componente principal, no es necesario realizar cambios adicionales:
// app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
})
export class AppComponent {}
En este ejemplo, hemos creado dos componentes: CardComponent
y AppComponent
. El componente CardComponent
es un componente genérico que muestra una tarjeta con un título y contenido personalizado. El componente AppComponent
es el componente principal que contiene dos instancias del componente CardComponent
con diferentes colores de fondo y estilos de letra.
Utilizamos la directiva *ngTemplateOutlet en el componente CardComponent
para insertar el contenido personalizado desde el componente AppComponent
. Las plantillas de contenido se definen dentro de las etiquetas
Ejemplo - trasclusión con objeto en lugar de plantilla
Es como el ejemplo anterior, pero le incorporamos un objeto, más completo.
- Primero, crea una interfaz para el objeto ContentData que contendrá la plantilla y el color del texto:
// content-data.interface.ts
import { TemplateRef } from '@angular/core';
export interface ContentData {
template: TemplateRef<any>;
textColor: string;
}
- A continuación, modifica el componente de tarjeta para que acepte un objeto ContentData en lugar de una TemplateRef directamente:
<!-- card.component.html -->
<div class="card" [ngStyle]="{'background-color': backgroundColor}">
<h2></h2>
<ng-container *ngTemplateOutlet="contentData.template; context: {textColor: contentData.textColor}"></ng-container>
</div>
- Luego, en el archivo TypeScript del componente de la tarjeta, actualiza la entrada para recibir un objeto ContentData:
// card.component.ts
import { Component, Input } from '@angular/core';
import { ContentData } from '../interfaces/content-data.interface';
@Component({
selector: 'app-card',
templateUrl: './card.component.html',
styleUrls: ['./card.component.scss'],
})
export class CardComponent {
@Input() title: string;
@Input() backgroundColor: string;
@Input() contentData: ContentData;
}
- A continuación, actualiza el componente principal (AppComponent) para pasar un objeto ContentData a cada componente de tarjeta:
<!-- app.component.html -->
<div style="display: flex">
<ng-template #content1 let-textColor="textColor">
<p [style.color]="textColor">Este es el contenido personalizado con letras rojas.</p>
</ng-template>
<ng-template #content2 let-textColor="textColor">
<p [style.color]="textColor">Este es el contenido personalizado con letras azules.</p>
</ng-template>
<app-card title="Tarjeta 1" backgroundColor="lightblue" [contentData]="{ template: content1, textColor: 'red' }"></app-card>
<app-card title="Tarjeta 2" backgroundColor="lightgreen" [contentData]="{ template: content2, textColor: 'blue' }"></app-card>
</div>
- No es necesario realizar cambios adicionales en el archivo TypeScript del componente principal:
// app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
})
export class AppComponent {}
En este ejemplo, hemos creado una interfaz ContentData
que contiene una referencia a la plantilla y el color del texto. Luego, ajustamos los componentes CardComponent
y AppComponent
para utilizar esta nueva estructura de datos.
El componente CardComponent
ahora acepta un objeto ContentData
como entrada en lugar de una TemplateRef directamente. En el componente AppComponent
, creamos objetos ContentData
para cada tarjeta y los pasamos como entrada a los componentes CardComponent
.
escríbe algo en comentarios
😉 Gracias.