iT邦幫忙

2024 iThome 鐵人賽

DAY 15
0
JavaScript

Signal API in Angular系列 第 15

Day 15 - 在 Directive Composition API 中使用 Signal 和 Signal Input

  • 分享至 

  • xImage
  •  

第 14 天,我示範了host element可以將propertiesattributes綁定到signalsignal inputsignal input也可以綁定到host directivehost properties,就像它是組件的一部分一樣。

在我的例子中,我展示了 Directive Composition API 如何將signalsignal input綁定到host directive並更新組件的 CSS 樣式。

例子 1:按下按鍵時更新 CSS 樣式

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 15';
 description = 'Bind signal inputs to Directive Composition API';

 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: '' });
}

App 組件顯示九個分別帶有字母 a、s、d、f、g、h、j、k、 l 的方塊。 當使用者按下上述任何鍵時,對應的方塊會變更 CSS 樣式。此組件偵聽 keyup 事件並呼叫 toSignal 以建立 key signal。 signal值綁定到 KeyComponentisPressed input。當字母與按下的鍵相符時,bgColor 也具有不同的背景顏色。我使用 Angular 18 的 let 語法來計算 expression 並將結果指派給 local template variable。

建立一個 KeyPressedDirective 指令

import { computed, Directive, input } from "@angular/core";

@Directive({
 selector: '[appKeyPressed]',
 standalone: true,
 host: {
   '[style.background-color]': 'bgColor()',
   '[class.hit]': 'isPressed()',
   '[attr.label]': 'ariaLabel()',
   '[style]': 'styleObject()',
 },
})
export class KeyPressedDirective {
 value = input.required<string>();
 isPressed = input.required<boolean>();
 ariaLabel = input.required<string>();
 bgColor = input.required<'yellow' | 'white'>();

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

KeyPressedDirective directive有四個 signal inputs 和一個 computed signalbackground-color style 屬性綁定到黃色或白色的 bgColor input。 Host class, hit, 綁定到 isPressed input 以變更邊框顏色、邊框寬度和邊框半徑。 label attribute 綁定到 ariaLabel input 並顯示 "{letter} key"。當您檢查host element時,會看到 label attribute。 style 物件綁定到 styleObject computed signal 以更改文字顏色並增加字體大小。

在 KeyComponent 中註冊 host directive

@Component({
 selector: 'app-key',
 standalone: true,
 hostDirectives: [
   {
     directive: KeyPressedDirective,
     inputs: ['value', 'isPressed', 'bgColor', 'ariaLabel']
   }
 ],
})

KeyComponent 組件在 hostDirective 屬性中註冊 KeyPressedDirective directive。 host 有四個 inputs,分別是 valueisPressedbgColorariaLabel

將 host styles 新增至 KeyComponent

styles: `
   :host {
     display: flex;
     justify-content: center;
     border: 1px solid black;
     width: 100px;
     height: 100px;
   }

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

host element 將字母放置在 flex container 的中心。 hit class 更改邊框顏色、邊框寬度和邊框半徑。

在 KeyComponent 中註入 host directive

import { Component, ChangeDetectionStrategy, inject } from '@angular/core';
import { KeyPressedDirective } from './key-pressed.directive';

@Component({
 selector: 'app-key',
 standalone: true,
 template: `
   <div>
     <span>{{ directive.value() }}</span>
   </div>
 `,
})
export default class KeyComponent {
 directive = inject(KeyPressedDirective);
}

KeyComponent 組件注入 KeyPressedDirective 指令 , 並在 HTML 範本中顯示 signal input。

例子 2:在 PhotoComponent 中使用 KeypressedDirective 指令

import { Component, ChangeDetectionStrategy, inject, input } from '@angular/core';
import { KeyPressedDirective } from './key-pressed.directive';

@Component({
 selector: 'app-photo',
 standalone: true,
 template: `
   <div class="photo">
     <img [src]="img()" [alt]="directive.ariaLabel()" />
     <p>{{ directive.value() }}</p>
   </div>
 `,
 hostDirectives: [
   {
     directive: KeyPressedDirective,
     inputs: ['value', 'isPressed', 'bgColor', 'ariaLabel:alt']
   }
 ],
 styles: `
   :host {
     display: flex;
     justify-content: center;
     border: 0.6rem solid black;
     Border-radius: 0.75rem;
   }

   :host-context(.hit) {
     border-color: orange;
     border-style: dashed
   } `,
})
export default class PhotoComponent {
 directive = inject(KeyPressedDirective);
 img = input.required<string>();
}

PhotoComponent 組件中重複使用 KeyPressedDirective 指令。此組件注入 host directive,並顯示值和圖像替代文字 (image alt text)。

// main.ts

<div class="photo">
     <app-photo [alt]="alt()" [value]="photoDescription()"
       [bgColor]="bgColor()" [isPressed]="isOn()" [img]="img()"  />
     <button (click)="toggle()">Change</button>
</div>
const base = 'https://picsum.photos/200?random='

alt = signal('A random picture');
photoDescription = signal('Lorem ipsum dolor sit amet, consectetur adipiscing elit. In quis tincidunt nibh. Fusce euismod scelerisque est sit amet sagittis. Nulla.');
isOn = signal(false);
img = signal(`${base}1`);

bgColor = computed(() => this.isOn() ? 'yellow' : 'white');

toggle() {
   this.isOn.update((prev) => !prev);
   this.img.set(`${base}${Date.now()}`);
}

PhotoComponent 組件匯入到 App 組件中。當您按一下 Change 按鈕時,isOn signal 會切換其值,img signal 會設定新的 URLbgColor signal 會重新計算背景顏色。 當發生 change detection 時,PhotoComponent 組件會更新圖片、背景顏色和邊框樣式。

結論:

  • Directive Composition API 允許 directive 成為組件的一部分。
  • Host directive可以將屬性和屬性綁定到signalsignal input。當 signal input 更新時, host element 也會更新。
  • 組件可以注入 host directive 來存取其 signal inputs
  • Host directive 可以在許多組件中註冊以供重複使用。

鐵人賽的第15天就這樣結束了。

參考:

-- Composition API Documentation: https://angular.dev/guide/directives/directive-composition-api


上一篇
Day 14 - Use signal and signal inputs in a host element
下一篇
Day 16 - 組件與model input之間的溝通
系列文
Signal API in Angular36
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言