👋 Vamos a explorar el fascinante mundo de las directivas - ¡esos pequeños hechizos que transforman vuestro HTML aburrido en interfaces interactivas y dinámicas!
🔮 Tipos de Directivas: ¡Conócelos todos!
Angular nos ofrece tres tipos mágicos de directivas (¡como si fueran las casas de Hogwarts! 😉):
- Directivas de Componentes 🏠 - ¡Son las más conocidas! Básicamente cualquier componente que creas.
@Component({
selector: 'app-magic',
template: '¡Abracadabra!'
})
¿Por qué se consideran directivas? ¡Porque cuando usas
en tu HTML, estás dando instrucciones al DOM!
- Directivas Estructurales 🧱 - Estas cambian la estructura de tu página como por arte de magia.
*ngIf="tienePermiso">¡Contenido secreto revelado!
*ngFor="let poción of pociones">{{ poción.nombre }}
@if (tienePermiso) {
¡Contenido secreto revelado!
}
@for (poción of pociones; track poción.id) {
{{ poción.nombre }}
}
¡Fíjate en el asterisco *
o el nuevo @
! Son como varitas mágicas que indican que algo va a aparecer o desaparecer.
🌈 Directivas Estructurales Integradas en Angular 🌈
1. @if / *ngIf 🚦
Esta directiva es como un guardia de seguridad que decide si un elemento puede entrar a la fiesta del DOM o no.
*ngIf="usuario.esPremium">¡Contenido exclusivo para ti!
@if (usuario.esPremium) {
¡Contenido exclusivo para ti!
} @else {
¡Hazte premium para ver este contenido!
}
¡La nueva sintaxis @if/@else es mucho más clara que el viejo *ngIf con ng-template para el else!
2. @for / *ngFor 🔄
¡El mago duplicador! Toma un elemento y lo clona para cada item de una colección.
*ngFor="let hechizo of hechizos; let i = index; trackBy: trackByFn">
{{i+1}}. {{hechizo.nombre}}
@for (hechizo of hechizos; track hechizo.id; let i = $index) {
{{i+1}}. {{hechizo.nombre}}
} @empty {
¡No conoces ningún hechizo todavía!
}
¡La nueva sintaxis @for incluye @empty para cuando la lista está vacía! ¡Y el tracking es más sencillo!
3. @switch / *ngSwitch 🔀
Como un interruptor mágico que elige entre diferentes opciones.
[ngSwitch]="poción.tipo">
*ngSwitchCase="'curación'">Recupera 50 HP
*ngSwitchCase="'fuerza'">+10 de ataque
*ngSwitchDefault>Efecto desconocido
@switch (poción.tipo) {
@case ('curación') {
Recupera 50 HP
}
@case ('fuerza') {
+10 de ataque
}
@default {
Efecto desconocido
}
}
¡La nueva sintaxis es mucho más parecida a un switch de JavaScript!
4. @defer 🚀 (¡Novedad en Angular 17!)
¡La directiva del futuro! Permite cargar contenido de forma perezosa o bajo demanda.
@defer {
} @loading {
Cargando mapa...
} @error {
¡Ups! No se pudo cargar el mapa
}
@defer (on viewport) {
}
@defer (on timer(5s)) {
}
¡@defer es como magia avanzada! Permite cargar componentes solo cuando son necesarios, mejorando el rendimiento.
🎨 Directivas de Atributo Integradas 🎨
1. [ngClass] 👕
¡El estilista mágico! Añade o quita clases CSS según condiciones.
[ngClass]="{'mago-activo': usuario.conectado, 'mago-durmiente': !usuario.conectado}">
Estado del mago
[class.mago-activo]="usuario.conectado" [class.mago-durmiente]="!usuario.conectado">
Estado del mago
2. [ngStyle] 🖌️
¡El pintor instantáneo! Aplica estilos CSS directamente.
[ngStyle]="{'color': hechizo.color, 'font-size.px': hechizo.poder * 5}">
{{hechizo.nombre}}
3. [ngModel] 📝
¡El escriba mágico! Crea un vínculo bidireccional entre un control de formulario y una variable.
[(ngModel)]="mago.nombre" placeholder="Nombre del mago">
¡Saludos, {{mago.nombre}}!
¡El famoso "banana in a box" [(ngModel)]! Combina ngModel y (ngModelChange) (event binding).
🔨 ¡Creemos nuestra propia directiva! 🚀
Imaginemos que queremos una directiva que haga que los elementos brillen cuando pasamos el ratón encima. ¡Vamos a crearla!
Paso 1: Invocamos al generador mágico 🧙♂️
ng generate directive directives/brillo
# O la versión corta para magos avanzados
ng g d directives/brillo
Paso 2: Preparamos nuestro hechizo ✨
@Directive({
selector: '[appBrillo]' // 👈 ¡Así lo invocaremos en nuestro HTML!
})
export class BrilloDirective {
@Input() colorBrillo = 'gold'; // 🌟 Color predeterminado
constructor(
private el: ElementRef, // 📌 Referencia al elemento
private renderer: Renderer2 // 🎮 El control remoto del DOM
) {}
@HostListener('mouseenter') // 🐭 Cuando el ratón entra...
onMouseEnter() {
this._aplicarBrillo(this.colorBrillo);
}
@HostListener('mouseleave') // 🐭 Cuando el ratón se va...
onMouseLeave() {
this._aplicarBrillo(null); // 💨 Quitamos el brillo
}
private _aplicarBrillo(color: string | null) {
// 🧙♂️ Magia pura: cambiamos el estilo sin tocar el DOM directamente
this.renderer.setStyle(
this.el.nativeElement,
'box-shadow',
color ? `0 0 10px ${color}` : null
);
this.renderer.setStyle(
this.el.nativeElement,
'transition',
'box-shadow 0.3s'
);
}
}
Paso 3: ¡A usarla! 🎉
appBrillo colorBrillo="purple">¡Este botón brilla en morado!
appBrillo>¡Yo brillo en dorado (valor por defecto)!
🧱 Directivas Estructurales: ¡Construyendo y demoliendo! 🏗️
Las directivas estructurales son como arquitectos del DOM. Vamos a crear una que haga lo contrario que *ngIf
(¡porque los rebeldes molan! 😎).
@Directive({
selector: '[appUnless]' // 👈 Se usará como *appUnless="condición"
})
export class UnlessDirective {
private hasView = false; // 👀 ¿Hemos creado ya la vista?
constructor(
private templateRef: TemplateRef<any>, // 📝 La plantilla a mostrar
private viewContainer: ViewContainerRef // 🧪 El contenedor donde mostrarla
) {}
@Input() set appUnless(condition: boolean) {
// 🔄 Lógica invertida respecto a *ngIf
if (!condition && !this.hasView) {
// ✅ Si la condición es falsa Y no hemos creado la vista
this.viewContainer.createEmbeddedView(this.templateRef);
this.hasView = true;
} else if (condition && this.hasView) {
// ❌ Si la condición es cierta Y tenemos una vista
this.viewContainer.clear(); // 🧹 Limpiamos el contenedor
this.hasView = false;
}
}
}
Uso (con ambas sintaxis):
*appUnless="estáLloviendo">¡Vamos a la playa! 🏖️
@unless (estáLloviendo) {
¡Vamos a la playa! 🏖️
}
🎯 Reglas de Oro del Profesor de Directivas 📚
- ¡No toques el DOM directamente! 🚫
// ❌ NUNCA hagas esto:
this.el.nativeElement.style.color = 'red';
// ✅ Usa siempre el Renderer2:
this.renderer.setStyle(this.el.nativeElement, 'color', 'red');
¿Por qué? Porque Angular puede ejecutarse en plataformas donde el DOM no existe (SSR, web workers).
- Nombres con prefijos claros 📋
// ❌ Mal: selector: '[highlight]'
// ✅ Bien: selector: '[appHighlight]'
Así evitamos colisiones con otras bibliotecas o directivas nativas.
- Una directiva = Una responsabilidad 🎯
// ❌ Mal: Una directiva que cambia color Y añade animación Y modifica texto
// ✅ Bien: Directivas separadas para cada función
¡Divide y vencerás! Las directivas pequeñas son más reutilizables.
- Documenta tus creaciones mágicas 📝
/**
* Aplica un efecto de brillo cuando el usuario pasa el ratón sobre el elemento.
* @param colorBrillo - Color del efecto (por defecto: gold)
* @example Contenido
*/
Tus compañeros (y tu yo del futuro) te lo agradecerán.
- Prueba tus hechizos antes de lanzarlos 🧪
describe('BrilloDirective', () => {
// Configuración de pruebas...
it('debe añadir brillo al pasar el ratón', () => {
triggerEventHandler('mouseenter', {});
expect(element.style.boxShadow).toContain('gold');
});
});
¡Un mago responsable siempre prueba sus hechizos!
🎓 ¡Tarea para casa! 📝
Ahora que ya sois aprendices de magos de las directivas, os propongo este desafío:
- Crea una directiva
appCuentaClics
que cuente cuántas veces se ha hecho clic en un elemento. - Debe mostrar un pequeño contador en la esquina superior derecha.
- Bonus: Añade un método para reiniciar el contador.
¡Recordad compartir vuestros hechizos en la próxima clase! 🧙♂️✨
¿Preguntas? ¡Levantad vuestra mano mágica! 🪄