Las directivas de Angular ofrecen una excelente manera de encapsular comportamientos reutilizables: las directivas pueden aplicar atributos, clases CSS y escuchadores de eventos a un elemento.
La API de composición de directivas te permite aplicar directivas al elemento host de un componente desde dentro de la clase TypeScript del componente.
Añadiendo directivas a un componente
Se aplican directivas a un componente añadiendo una propiedad hostDirectives al decorador de un componente.
Llamamos a tales directivas directivas host.
En este ejemplo, aplicamos la directiva MenuBehavior
al elemento host de AdminMenu. Esto funciona de manera similar a aplicar MenuBehavior al elemento <admin-menu> en una plantilla.
@Component({ selector: 'admin-menu', template: 'admin-menu.html', hostDirectives: [MenuBehavior],})export class AdminMenu { }
Cuando el framework renderiza un componente, Angular también crea una instancia de cada directiva host. Los enlaces host de las directivas se aplican al elemento host del componente. Por defecto, las entradas y salidas de las directivas host no se exponen como parte de la API pública del componente. Consulta Incluyendo entradas y salidas a continuación para más información.
Angular aplica las directivas host estáticamente en tiempo de compilación. No puedes añadir dinámicamente directivas en tiempo de ejecución.
Las directivas usadas en hostDirectives no pueden especificar standalone: false.
Angular ignora el selector de las directivas aplicadas en la propiedad hostDirectives.
Incluyendo entradas y salidas
Cuando aplicas hostDirectives a tu componente, los inputs y outputs de las directivas host
no se incluyen en la API de tu componente de manera predeterminada.
Puedes incluir explícitamente esos inputs y outputs expandiendo la entrada en hostDirectives:
@Component({ selector: 'admin-menu', template: 'admin-menu.html', hostDirectives: [{ directive: MenuBehavior, inputs: ['menuId'], outputs: ['menuClosed'], }],})export class AdminMenu { }
Al especificar explícitamente los inputs y outputs, los consumidores del componente con
hostDirective pueden enlazarlos en una plantilla:
<admin-menu menuId="top-menu" (menuClosed)="logMenuClosed()">
Además, puedes asignar alias a los inputs y outputs de hostDirective para personalizar la API
de tu componente:
@Component({ selector: 'admin-menu', template: 'admin-menu.html', hostDirectives: [{ directive: MenuBehavior, inputs: ['menuId: id'], outputs: ['menuClosed: closed'], }],})export class AdminMenu { }
<admin-menu id="top-menu" (closed)="logMenuClosed()">
Añadiendo directivass a otra directiva
También puedes agregar hostDirectives a otras directivas, además de a componentes.
Esto habilita la agregación transitiva de múltiples comportamientos.
En el siguiente ejemplo, definimos dos directivas, Menu y Tooltip.
Luego componemos el comportamiento de estas dos directivas en MenuWithTooltip.
Finalmente, aplicamos MenuWithTooltip a SpecializedMenuWithTooltip.
Cuando SpecializedMenuWithTooltip se usa en una plantilla, crea instancias de Menu, Tooltip
y MenuWithTooltip. Los host bindings de cada una de estas directivas se aplican al elemento host
de SpecializedMenuWithTooltip.
@Directive({...})export class Menu { }@Directive({...})export class Tooltip { }// MenuWithTooltip puede componer comportamientos de múltiples directivas@Directive({ hostDirectives: [Tooltip, Menu],})export class MenuWithTooltip { }// CustomWidget puede aplicar los comportamientos ya compuestos de MenuWithTooltip@Directive({ hostDirectives: [MenuWithTooltip],})export class SpecializedMenuWithTooltip { }
Semántica de las directivas host
Orden de ejecución de las directivas
Las directivas host siguen el mismo ciclo de vida que los componentes y directivas usadas directamente en una plantilla. Sin embargo, las directivas host siempre ejecutan su constructor, hooks de ciclo de vida y bindings antes que el componente o directiva sobre el cual se aplican.
El siguiente ejemplo muestra el uso mínimo de una directiva host:
@Component({ selector: 'admin-menu', template: 'admin-menu.html', hostDirectives: [MenuBehavior],})export class AdminMenu { }
El orden de ejecución aquí es:
- Se instancia
MenuBehavior - Se instancia
AdminMenu MenuBehaviorrecibe entradas (ngOnInit)AdminMenurecibe entradas (ngOnInit)MenuBehavioraplica host bindingsAdminMenuaplica host bindings
Este orden de operaciones significa que los componentes con hostDirectives pueden sobrescribir
cualquier host binding especificado por una directiva host.
Este orden también se aplica a cadenas anidadas de directivas host, como se muestra en el siguiente ejemplo:
@Directive({...})export class Tooltip { }@Directive({ hostDirectives: [Tooltip],})export class CustomTooltip { }@Directive({ hostDirectives: [CustomTooltip],})export class EvenMoreCustomTooltip { }
En el ejemplo anterior, el orden de ejecución es:
- Se instancia
Tooltip - Se instancia
CustomTooltip - Se instancia
EvenMoreCustomTooltip Tooltiprecibe entradas (ngOnInit)CustomTooltiprecibe entradas (ngOnInit)EvenMoreCustomTooltiprecibe entradas (ngOnInit)Tooltipaplica host bindingsCustomTooltipaplica host bindingsEvenMoreCustomTooltipaplica host bindings
Dependency injection
Un componente o directiva que especifica hostDirectives puede inyectar las instancias de esas
directivas host y viceversa.
Cuando aplicas directivas host a un componente, tanto el componente como las directivas host pueden definir providers.
Si un componente o directiva con hostDirectives y esas directivas host proveen el mismo token de
inyección, los providers definidos en la clase con hostDirectives tienen precedencia sobre
los providers definidos en las directivas host.