iT邦幫忙

2024 iThome 鐵人賽

DAY 14
0
JavaScript

Signal API in Angular系列 第 14

Day 14 - Use signal and signal inputs in a host element

  • 分享至 

  • xImage
  •  

In angular.dev official documentation, I found the following statement

Always prefer using the host property over @HostBinding and @HostListener. These decorators exist exclusively for backwards compatibility.

HostBinding allows us to bind host properties and attributes to properties and methods.

@Component({
  /* ... */
})
export class CustomSlider {
  @HostBinding('attr.aria-valuenow')
  value: number = 0;

  @HostBinding('tabIndex')
  getTabIndex() {
    return this.disabled ? -1 : 0;
  }
  /* ... */
}

HostListener allows us to bind host event listeners to methods.

export class CustomSlider {
  @HostListener('keydown', ['$event'])
  updateValue(event: KeyboardEvent) {
    /* ... */
  }
}

I suppose host properties are preferred because they can work with signals and signal inputs. On the other hand, HostBinding and HostListener, are designed to work with primitives and methods.

In my demo, I showed how to bind signal inputs to style object, style property, class property, and attributes.

Setup codes for the demo

import { Component, signal } from '@angular/core';
import { filter, fromEvent, map } from 'rxjs';
import { toSignal } from '@angular/core/rxjs-interop';
import KeyComponent from './key.component';

@Component({
 selector: 'app-root',
 standalone: true,
 imports: [KeyComponent],
 template: `
   <h1>Hello from {{ name }}!</h1>
   <h2>{{ description }}</h2>
   <div class="container">
     @for(v of keys(); track v) {
       @let isPressed = key() === v;
       @let bgColor = isPressed ? ‘yellow’ : ‘white’;
       <app-key [value]="v" [isPressed]="isPressed"
         [ariaLabel]="v + ' key'" [bgColor]="bgColor"
       />
     }
   </div>
 `,
})
export class App {
 name = 'iTHome Ironman 2024 day 14';
 description = 'Bind signal inputs to host element';

 keys = signal('asdfghjkl');
 key = toSignal(fromEvent(document, 'keyup').pipe(
   filter((evt) => evt instanceof KeyboardEvent),
   map((evt) => evt as KeyboardEvent),
   map((evt) => evt.key.toLowerCase()),
   filter((key) => 'asdfghjkl'.indexOf(key) >= 0)
 ), { initialValue: '' });
}

The App component displays nine squares with letters a, s, d, f, g, h, j, k l respectively. When a user presses any of the above keys, the corresponding square changes the CSS styling. The component listens to a keyup event and calls toSignal to create a key signal. The signal value is binded to the KeyComponent's isPressed input. When the letter matches the key pressed, the bgColor also has a different background color. I use Angular 18's let syntax to evaluate the expressions and assign the results to the local template variables.

Bind signal inputs to a host element

<div>
   <span>{{ value() }}</span>
</div>

The KeyComponent displays the letter in the template.

styles: `
   :host {
     display: flex;
     justify-content: center;
     align-items: center;
     border: 1px solid black;
     font-size: 2rem;
     width: 100px;
     height: 100px;
   }

:host-context(.hit) {
     border-color: rebeccapurple;
     border-width: 0.4rem;
     border-radius: 0.5rem;
}` 

The host element specifies a flex box to place the letter at the center of the container. It also has a hit CSS class to change the border color, width, and radius.

host: {
   '[style.background-color]': 'bgColor()',
   '[class.hit]': 'isPressed()',
   '[attr.label]': 'ariaLabel()',
   '[style]': styleObject()',
},

The host property binds style property, class name, attribute, and style object to signal inputs and a computed signal.

export default class KeyComponent {
 value = input.required<string>();
 isPressed = input.required<boolean>();
 ariaLabel = input.required<string>();
 bgColor = input.required<string>();

 styleObject = computed(() => {
   const fontSize = this.isPressed() ? 2.5 : 2;
   const color = this.isPressed() ? 'cyan' : 'black';
   return {
     fontSize: `${fontSize}rem`,
     color,
   }
 });
}

The KeyComponent component has four signal inputs and a computed signal. The background-color style property binds to the bgColor input to have either yellow or white color. The host class, hit, binds to the isPressed input to change the border color, width and radius. The label attribute binds to the ariaLabel input and displays "{letter} key". When you inspect the host element, the attribute value is seen. The [style] object binds to the styleObject computed object to change the text color and increase the font size.

Conclusions:

  • Angular team recommends host property over HostListener and HostBinding
  • Host properties can bind style property, class name, attribute, style object, class object, and event to signals and signal inputs.

This wraps up day 14 of the ironman challenge.

References:

Host Element Documentation: https://angular.dev/guide/components/host-elements#binding-to-the-host-element

Demo: https://stackblitz.com/edit/stackblitz-starters-reurma?file=src%2Fkey.componen.ts


上一篇
Day 13 - 將路由資料綁定到 Signal Input
下一篇
Day 15 - 在 Directive Composition API 中使用 Signal 和 Signal Input
系列文
Signal API in Angular36
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言