首頁  >  文章  >  web前端  >  我如何使用訊號來建立我的 Angular 元件

我如何使用訊號來建立我的 Angular 元件

Patricia Arquette
Patricia Arquette原創
2024-10-09 06:23:29586瀏覽

How I structure my Angular components with Signals

在這篇短文中,我想向您展示我如何使用訊號建立我的元件,而不需要外部函式庫。當然,像 NgRx 這樣的東西將發揮巨大的作用,使我們的程式碼更加健壯,但讓我們從簡單的開始!

首先,我用訊號定義所有狀態:

export class TodoListComponent {
  todos = signal<Todo[]>([]);
}

這也適用於輸入!如果我的元件需要輸入,我會使用 Angular 的新 input() 函數來宣告它,這也會給我一個訊號。如果它剛好是路由參數,我使用 input.required().

然後,如果我想顯示可以從另一個狀態匯出的某種狀態,我總是使用計算:

completedTodos = computed(() => this.todos().filter(t => t.completed));

然後,如果你了解我,你就知道我有多鄙視直接在類別方法中執行非同步副作用......?

export class TodoListComponent {
  todoService = inject(TodoService);

  toggleTodo(id: string) {
    this.todoService.toggle(id).subscribe(newTodo => ...);
  }
}

為什麼這麼問?因為如果該方法是直接引發副作用的方法(在本例中為呼叫 subscribe),則您無法控制背壓

背壓可以總結為:如果使用者在上一個呼叫尚未完成時切換待辦事項,會發生什麼事?

有很多問題,例如:

  1. 我們是否想執行第二次呼叫?還是等第一個完成?還是我們應該取消第一個?
  2. 如果我們在短時間內切換不同的項目會怎麼樣?
  3. 如果我們想引入某種去抖油門怎麼辦?

如果您了解 RxJS(如果您正在閱讀本文,那麼您現在應該知道了!)您知道第一個問題可以透過 4 個扁平化運算子(mergeMap、concatMap、switchMap、exhaustMap)輕鬆解決。

然後,如果你非常了解 RxJS,你就會知道你可以使用一個名為 groupBy 的很棒的運算子來解決第二個問題!

但是為了利用所有這些優點,你必須有一個 Observable 來源,所以......不是一個方法。

科目

將主題想像成一個開放的(未完成的)空的 Observable。它是表示自訂事件的完美工具。

我們元件中的所有事件都可以用主題來表示:

export class TodoListComponent {
  ...

  toggleTodo$ = new Subject<string>();
  deleteTodo$ = new Subject<string>();
  addTodo$ = new Subject<void>();
}

然後,我們的範本可以在必要時簡單地呼叫它們,而不是呼叫方法,例如:

<button (click)="deleteTodo$.next(todo.id)">delete</button>

現在我們的來源是 Observables,我們可以使用我們親愛的運算符:讓我們創建一些效果

效果

我喜歡在建構函式中定義我的效果,這樣我就可以在元件被銷毀時使用 takeUntilDestroyed() 運算子來清理效果!因此,例如:

constructor() {
  this.addTodo$.pipe(
    concatMap(() => this.todoService.add())
    takeUntilDestroyed()
  ).subscribe(newTodo => this.todos.update(todos => [...todos, newTodo]));
}

這裡我使用 concatMap 來保留回應的順序,以便依序新增待辦事項。這意味著沒有並發調用。我認為它對於添加操作是完美的,但對於其他呼叫來說可能是錯誤的選擇:例如,對於 GET 請求,通常最好使用 exhausMap 或 switchMap,具體取決於用例。

我還使用了一種稱為悲觀更新的方法,這意味著我等待通話結束來更新我的內部狀態。這是個人喜好!您可以立即新增待辦事項,然後在 API 呼叫出錯時使用 catchError 將其還原。

然後是 Angular 的實際效果函數,它旨在與訊號結合使用:我使用此函數來執行同步任務。例如,當 URL 中的參數發生變更(指新的實體 ID)時,我可能會想要使用新實體更新表單:

// This comes from the router
id = input.required<string>();

// Always stores the current invoice information
currentInvoice = toSignal(toObservable(this.id).pipe(
  switchMap(id => this.invoiceService.get(id))
));

constructor() {
  effect(() => {
    // Assuming the 2 structures match, every time we browse
    // to a new invoice, the form gets populated
    this.form.patchValue(this.currentInvoice());
  })
}

請注意,我們無法使用此技術控制背壓。對於這種事情,這沒問題,但請記住:這就是為什麼我們仍然需要 RxJS 來製作無錯誤的應用程式。或者另一個庫在底層抽象化了這種複雜性。

完全響應式並不總是一個好主意

我們用訊號表示的許多狀態在技術上可以被視為派生的非同步狀態。例如,我們的待辦事項清單可以被視為來自伺服器的派生狀態:

// Trigger this when you need to refetch the todos
fetchTodos$ = new Subject<void>();

todos = toSignal(toObservable(this.fetchTodos$).pipe(
  switchMap(id => this.todoService.getAll())
));

這種方法類似於 TanStack Query 等庫所使用的方法,在這種方法中,當您需要新資料時,您可以手動使查詢無效。換句話說,每次突變你總是去伺服器。

這在某些情況下可能很好,但有兩件事需要考慮:

  1. 這使得手動更新狀態(樂觀更新)變得困難。 TanStack Query 等庫使這變得更容易,但手動執行此操作很痛苦。
  2. 這使得大多數開發人員更難掌握程式碼,這就是我作為每天從事此類工作的顧問所看到的。

總之,我通常不推薦。我說通常! :)

結論

我希望你喜歡這篇短文!總結一下:

  • 將您的狀態定義為訊號
  • 將派生狀態定義為計算訊號
  • 將非同步效果定義為 Observables
  • 用效果定義您的同步效果

我確信,如果您遵循這些原則,您的應用程式將會更容易維護!

以上是我如何使用訊號來建立我的 Angular 元件的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn