Angular v20 更新重點分享

header

最低版本限制

Angular Node.js TypeScript
20.0.x ^20.19.0 || ^22.12.0 || ^24.0.0 >=5.8.0 <5.9.0

使用pnpm outdated可查看可更新套件

舊專案升版

使用ng-update,但無法一次跨兩版,需分階進行v18->v19->v20

官方更新指南

header

Signal API 趨於穩定

signal、computed、input、effect、linkedSignal、toSignal 皆已進入穩定版本。原先使用裝飾器的語法建議改為新的signal語法

原有方法 signal方法
@Input input
@Output output
@Input/@Output model
@ViewChild viewChild
@ViewChildren viewChildren
header

基礎Signal

  • signal
  • 替換&更新
    • set - 直接改值,單純覆蓋
    • update - 當前值作為參數,可基於舊值更新
  • computed - 計算屬性,派生signal,唯獨
  • effect - 副作用,響應式執行,只能存在inject context

stackblitz 範例-基礎Signal

header

Signal Input / Output / model input

input1 = input(0); //含有預設值
input2 = input<string>(); //無預設值 undefined
input3 = input.required<number>(); //強制要求一定要於模板使用輸入
input4 = input('', {alias: 'text', transform: trimStr}); //別名與轉換函式

dialogClose = output(); //void
target = output<number>();

visible = model(true); //於模板中使用[()]雙向綁定

trimStr(value: string){
  return value.trim();
}
closeDialog(){
  this.dialogClose.emit();
}
clickTarget(id: number){
  this.target.emit(id); //傳遞事件資料
}

stackblitz 範例-input/output/model

header

LinkedSignal

  shippingOptions = signal<ShippingMethod[]>([
    { id: 0, name: 'Ground' },
    { id: 1, name: 'Air' },
    { id: 2, name: 'Sea' },
  ]);

  selectedOption = linkedSignal<ShippingMethod[], ShippingMethod>({
    // `selectedOption` is set to the `computation` result whenever this `source` changes.
    source: this.shippingOptions,
    computation: (newOptions, previous) => {
      return (
        newOptions.find((opt) => opt.id === previous?.value.id) ?? newOptions[0]
      );
    },
  });

stackblitz 範例 - linkedSignal

header

linkedSignal 與 computed 的差別

兩者皆由其他 signal 派生而來,但linkedSignal 不同的點在於他是writable signal, 可以直接對其進行值的變更,且允許存取先前的值。

image
image

header

httpResource API - 基於 signal 的請求-1

【此部分仍處於實驗性 api】

v19 導入了 resource api,Resource 提供了一種將非同步資料合併到應用程式基於訊號的程式碼中的方法

v20推出httpResource() API,提供了一種宣告式的、基於 Signal 的方式來處理 HTTP GET 請求。

能自動管理載入中 (loading)、錯誤 (error) 和資料 (data) 狀態

httpResource() 在底層使用 HttpClient。

header

httpResource API - 基於 signal 的請求-2

@Component({
  selector: 'app-user-profile',
  template: `
    @switch (userResource.state()) {
      @case ('loading') { <p>Loading...</p> }
      @case ('error') { <p>Error fetching user data</p> }
      @case ('resolved') { 
        <pre>{{ userResource.value() | json }}</pre> 
      }
    }`
})
export class UserProfile {
  userId = signal(1);
  userResource = httpResource<User>(() => 
    `https://example.com/v1/users/${this.userId()}`
  );
}
header

resource & httpResource showcase

如果一個請求已經處於待處理狀態,會自動先取消未完成的請求,然後再發出新的請求

stackblitz 範例-resource & httpResource

header

zoneless

zoneless已經是預覽版本

  • 新專案 - 使用 cli 建立專案時可以選擇採用zoneless
  • 舊專案調整如下
export const appConfig: ApplicationConfig = {
  providers: [
    provideBrowserGlobalErrorListeners(),
    provideZonelessChangeDetection(),
  ]
};

從 angular.json 移除 zone.js polyfill, 解安裝npm uninstall zone.js

header

zoneless 後變更偵測的時機

觸發時機 說明
在模板中綁定 DOM 事件(例如 (click)) 當使用者與畫面互動時(如點擊按鈕)會觸發變更偵測
Signal 被更新 當使用 signal 的值被改變時會觸發變更偵測
模板中使用了 async 管道(async pipe) 當 async pipe 訂閱的 Observable/Signal 發生變化時
使用 ComponentRef.setInput() 改變輸入參數 手動設定元件輸入屬性時會觸發變更偵測
手動呼叫 ChangeDetectorRef.markForCheck() 明確標記元件需要檢查變更
元件被建立或銷毀 新增或移除元件時,自動觸發變更偵測
header

動態建立元件 createComponent - 1

以往動態建立元件需要更改input主要有兩種方法

1.使用instance來變更 缺點:手動設定的input屬性不會觸發onChange生命週期

#vcr = inject(ViewContainer);
ngOnInit(){
    const ref = this.#vcr.createComponent(MyComponent);
    ref.instance.text = 'myText'
}

2.使用setInput()

#vcr = inject(ViewContainer);
ngOnInit(){
    const ref = this.#vcr.createComponent(MyComponent);
    ref.setInput('text', 'myText')
}
header

動態建立元件 createComponent - 2

新增inputBinding、outputBinding以及twoWayBinding,屬性綁定/指令更加容易

export class AppComponent {
 readonly vcr = viewChild.required('container', { read: ViewContainerRef });
 readonly canClose = signal(true)
 readonly isExpanded = signal(true)
 createWarningComponent(): void {
   this.vcr().createComponent(AppWarningComponent, {
       bindings: [
           inputBinding('canClose', this.canClose),
           twoWayBinding('isExpanded', this.isExpanded),
           outputBinding<boolean>('close', (isConfirmed) => console.log(isConfirmed))
       ],
       directives: [
           FocusTrap,
           {
               type: ThemeDirective,
               bindings: [inputBinding('theme', () => 'warning')]
           }
       ]
   })
 }
}
header

模板語法 Tagged Template Literals

帶標記模板,可以把文字跟變數分開提取出來進行轉換

Pipes 跟 Tagged template literals的差別: 雖兩者都是做到將值轉換,但Tagged template literals 可以直接存取元件的屬性跟方法

@Component({
 template: `<p>{{ greet`hello, ${name()}` }}</p>`,
})

export class AppComponent {
 name = signal('Jack');
 greet(strings: TemplateStringsArray, name: string){
   return `${strings[0]}${name}`;
  }
}
header

模板指數運算 **

現在可以於模板中使用指數運算符 {{ 2**3 }} =>8

支援 in 運算子

用來辨別物件是否含有指定屬性,僅判別是否有相符屬性,即便是undefined或是null

@Component({
 template: `@if('age' in person){ 有年齡屬性 }`,
})

export class AppComponent {

 person = {
     name: 'Jack',
     age: 18
 }
}
header

開發者工具更新

新增 show signal graph beta 可以圖形化顯示 signal 相依關係。

目前仍處於beta,預設關閉,需於設定面板中打開

Chrome 擴充套件

header

Control flow

過去的*ngIf 、 *ngFor 和 *ngSwitch 預計v22會被移除,建議都使用新的@if 、 @for等方法。

指令轉換:

ng generate @angular/core:control-flow
header

Host事件 建議改於 metadata 中設定

過去常用的方法

@HostListener('keydown', ['$event'])
onKeydown(event: KeyboardEvent){
    if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(event.key)) {
      this.#emitCaretPosition();
    }
}

現在推薦的方法

@Component({
  ...,
  host: {
    '[class.active]': 'isActive()',
    '(keydown)': 'updateValue($event)',
  },
})
header

Style Guide

預設已經不會為透過 cli gen 的元件服務等產生後綴,若要啟動加入後綴要於 angular.json 新增額外設定

{
  "projects": {
    "app": {
      "schematics": {
        "@schematics/angular:component": { "type": "component" },
        "@schematics/angular:directive": { "type": "directive" },
        "@schematics/angular:service": { "type": "service" },
        "@schematics/angular:guard": { "typeSeparator": "." },
        "@schematics/angular:interceptor": { "typeSeparator": "." },
        "@schematics/angular:module": { "typeSeparator": "." },
        "@schematics/angular:pipe": { "typeSeparator": "." },
        "@schematics/angular:resolver": { "typeSeparator": "." }
      },
}
header

AI輔助開發 - Angular cli Mcp server

輸入指令 ng mcp 即會產生mcp設定,vscode於專案下的 .vscode/mcp.json設定

{
  "servers": {
    "angular-cli": {
      "command": "npx",
      "args": ["@angular/cli", "mcp"]
    }
  }
}

可使用的Tool:
get_best_practices / search_documentation / list_projects

header

AI輔助開發 - Angular Best Practices Guide

官方提供之 Angular 最佳實踐的系統指令,以符合新語法與風格指南

官方提供之範本

header

給編輯器的規則文件

reference: https://angular.dev/ai/develop-with-ai

header

Angular 20 相關參考資料

header

Thanks!