Es una especie de viejo concepto hecho nuevo otra vez. En este video veremos un par de ejemplos que creé para videos anteriores sobre estos decoradores, y los reemplazaremos con los métodos más nuevos. También actualizaremos algunos otros conceptos en estos componentes y directivas. Bien, empecemos con un ejemplo de @HostBinding.
En las nuevas versiones de Angular, los decoradores @HostBinding y @HostListener ya no se pueden utilizar. Como dice la nueva documentación...
«existen exclusivamente para compatibilidad con versiones anteriores».
Uso de la vinculación de clases a elementos del host para reemplazar el decorador @HostBinding
Tenemos esta aplicación que fue creada originalmente para demostrar diferentes formas de enlazar clases en elementos.

Para uno de los ejemplos de este vídeo, he utilizado el decorador @HostBinding para enlazar condicionalmente una clase en el elemento host de este componente de formulario cuando el estado del campo de correo electrónico cambia de no válido a válido.
protected emailControl = new FormControl<string>('', {
nonNullable: true,
validators: [Validators.required, Validators.email]
});
@HostBinding('class.valid') isValid = false;
constructor(private destroyRef: DestroyRef, private renderer: Renderer2) {
}
Por lo tanto, si añado un correo electrónico válido aquí, puedes ver que el estilo cambia.

Ahora parte de esto es el cambio de la clase vinculada al host, y el resto es una clase que se conmuta en el cuerpo. Todo esto sucede en una suscripción observable al cambio de estado del control de correo electrónico.
constructor(private destroyRef: DestroyRef, private renderer: Renderer2) {
}
ngOnInit() {
this.emailControl.statusChanges
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(status => {
this.isValid = status === 'VALID';
status === 'VALID'
? this.renderer.addClass(document.body, 'valid')
: this.renderer.removeClass(document.body, 'valid');
});
}
Así que vamos a modernizar todo esto. Primero, podemos eliminar el decorador @HostBinding y podemos eliminar su importación también ya que no lo necesitaremos más.
Ok, lo que usaremos ahora es algo que existía hace tiempo, después de que cambiamos de AngularJS a Angular 2 o Angular moderno. Usaremos la vieja/nueva propiedad host en el decorador del componente.
En esta propiedad, podemos enlazar clases tal y como lo haríamos en la plantilla. Como estamos vinculando una clase «válida», usaremos corchetes para vincularla al atributo class. Y luego esta clase se vinculará a nuestra propiedad «isValid».
form.component.ts
@Component({
selector: 'app-form',
...
host: {
'[class.valid]': 'isValid'
}
})
export class FormComponent {
private isValid = false;
...
}
Ok, en este punto, lo que tenemos ahora es equivalente a lo que teníamos antes de quitar el decorador, pero todavía hay más que podemos hacer aquí.
Cómo Convertir el Valor Observable del Formulario en una Señal
Una cosa que podemos hacer es utilizar señales para enlazar directamente con el estado del valor del control de correo electrónico con la nueva función toSignal(). Esta función convertirá un observable en una señal.
Por lo tanto, necesitamos pasarle el observable de cambios de estado del control. Luego añadiremos una tubería, y mapearemos el estado para que podamos devolver un valor booleano basado en si el estado del control es válido o no.
private isValid = toSignal(this.emailControl.statusChanges
.pipe(map(status => { return status === 'VALID'; })));
Así que ahora esta propiedad «isValid» es una señal que se actualizará automáticamente cuando cambie el estado del control. Esto significa que tendremos que añadir paréntesis a la propiedad en nuestro enlace de clase.
Antes:
host: {
'[class.valid]': 'isValid'
}
Después:
host: {
'[class.valid]': 'isValid()'
}
Bien, ahora que el cambio de estado se ha convertido en una señal, podemos utilizar la nueva función effect( )
para alternar la clase válida en el cuerpo en lugar de la suscripción.
Cómo usar effect()
para alternar una clase cuando cambia el estado de un campo de formulario
Para hacer esto, agreguemos la función effect()
dentro del constructor. Entonces podemos copiar el código que activa la clase y pegarlo en effect()
.
A continuación, sólo tenemos que cambiar esta condición para utilizar la señal «isValid»
. Ahora esto se ejecutará cada vez que el valor de la señal «isValid»
cambie, por lo que no necesitaremos la antigua suscripción.
Tampoco necesitaremos más el método OnInit()
. También podemos eliminar el DestroyRef
. Entonces, podemos eliminar todas las importaciones también.
constructor(private renderer: Renderer2) {
effect(() => {
this.isValid()
? this.renderer.addClass(document.body, 'valid')
: this.renderer.removeClass(document.body, 'valid');
});
}
Ok, eso es todo lo que probablemente podemos cambiar aquí. Ahora debería verse y funcionar igual que antes, pero ahora todo está actualizado para funcionar de forma moderna en Angular sin el decorador @HostBinding.
Esta es la nueva forma de enlazar con el elemento host, pero ¿qué pasa con los eventos host que usan el decorador @HostListener? Bueno, esto también ha cambiado.
Uso de eventos de elemento anfitrión para sustituir al decorador @HostListener
Tenemos un ejemplo que, como la última demo, fue creado para demostrar diferentes formas de escuchar eventos en Angular.
En uno de los ejemplos utilicé el decorador @HostListener
para escuchar un evento click en el host de una directiva y emitir el evento utilizando el decorador @Output
y un EventEmitter
.
export class HostListenerDirective {
@Output() buttonClick = new EventEmitter<PointerEvent>();
@HostListener('click', ['$event']) private handleHostClick(event: PointerEvent) {
event.preventDefault();
this.buttonClick.emit(event);
}
}
Así, si simplemente hago clic en el botón «enviar», veremos un mensaje que indica que se ha hecho clic en el botón.

Así que al igual que el decorador @HostBinding
, podemos eliminar el @Hostlistener
porque ya no lo necesitamos. En su lugar volveremos a utilizar la propiedad host.
Y esta vez, como estamos vinculando a un evento, usaremos paréntesis. Cuando el evento se dispare, llamaremos a nuestra función handleHostClick()
y le pasaremos el evento click.
host-listener.directive.ts
@Directive({
selector: '[appHostListener]',
...
host: {
'(click)': 'handleHostClick($event)'
}
})
export class HostListenerDirective {
@Output() buttonClick = new EventEmitter<PointerEvent>();
private handleHostClick(event: PointerEvent) {
event.preventDefault();
this.buttonClick.emit(event);
}
}
Cómo convertir una salida usando el decorador @Output
a la nueva función output()
Bien, ahora que nos hemos deshecho del @HostListener
, también podemos actualizar esta salida para que utilice la nueva función output( )
También podemos eliminar el decorador @Output
y el EventEmitter
, ya que no son necesarios con la nueva función output()
. Entonces, podemos reemplazarlos por la nueva función output()
.
Antes:
@Output() buttonClick = new EventEmitter<PointerEvent>();
Después:
buttonClick = output<PointerEvent>();
Y todo lo demás permanece igual para esto, así que no necesitamos cambiar nada más.
Así que esto es todo lo que podemos actualizar en esta directiva, si guardáramos deberíamos ver que todo funciona igual que con el decorador @HostListener
, el decorador @Output
y el EventEmitter
, pero ahora todo está actualizado para funcionar de una forma moderna en Angular.
Conclusión
Bien, eso es todo. Ahora deberías tener una sólida comprensión de cómo enlazar y escuchar eventos en elementos anfitriones de componentes y directivas sin usar los viejos decoradores.