Skip to content
AITroveRead. Build. Understand.
Make this comfortable

Angular Signal Sources, Computed Views, and Effect Scope

Last updated: 7 Oct 20268 min read
tutorial
IntermediateBy AITrove Editorial

An Angular writable signal represents application state that a component can update. A computed signal reads source signals and produces a cached, read-only result; when its dependencies change, Angular invalidates the result and calculates it on the next read. An effect also tracks signal reads, but its job is to synchronize with an external system, not to copy one signal into another. Copying filtered rows and counts through effects introduces intermediate states and can create update loops. Signals can hold objects whose nested contents are still mutable JavaScript values, so a read-only signal reference does not make every field immutable. A reliable design declares which data owns the truth and how new snapshots replace old ones.

Working case

A permit queue has a cases signal, search signal, visibleRows signal, and visibleCount signal. Two effects propagate updates through the chain. When reviewer 47 types while a new batch arrives, one effect updates rows and the second has not yet updated count; a stale summary briefly appears above the list. Another effect writes a browser storage preference on every visible-row change, sending a large private dataset to local storage even though only the phrase should persist. The revised queue stores the authorized case snapshot and phrase as source signals, derives rows and count with computed signals, and uses a narrowly scoped storage effect for the small preference alone after confirming that browser storage is available.

Implementation boundary

typescript
import { Component, computed, signal } from "@angular/core";

type PermitSummary = { id: number; title: string };

@Component({
  selector: "permit-search",
  standalone: true,
  template: `<input type="search" aria-label="Find permit"
    [value]="phrase()" (input)="setPhrase($event)">
    <p>{{ visibleCount() }} permitted cases</p>`
})
export class PermitSearchComponent {
  readonly cases = signal<readonly PermitSummary[]>([]);
  readonly phrase = signal("");
  readonly visibleCases = computed(() => {
    const needle = this.phrase().trim().toLocaleLowerCase();
    return this.cases().filter(permitCase => permitCase.title.toLocaleLowerCase().includes(needle));
  });
  readonly visibleCount = computed(() => this.visibleCases().length);
  setPhrase(event: Event): void {
    this.phrase.set((event.target as HTMLInputElement).value);
  }
}

Start with the data contract: an authorized server snapshot, a local phrase, a selected case ID, and pending approvals. Put these in writable signals at their appropriate owner. Derive filtered rows and count from the snapshot and phrase; a computed getter must remain pure and must not send requests, mutate inputs, or write storage. Use immutable snapshot replacement when the server sends changes so a nested mutation cannot bypass the declared update boundary. If storage synchronization is required, create an effect in a valid injection context and register cleanup for any timer or external subscription it starts. Do not use an effect merely to push one signal value into another. For an expensive filter, measure recomputation and rendered rows separately before adding a search index, worker, or view window. Make the server authoritative for permissions; a phrase only narrows an already authorized list.

Cost and boundaries

A linear filter over 47,000 cases takes O(n) work when its inputs change and may allocate O(n) references. Computed caching saves repeated reads while dependencies stay stable, but it cannot make every changed phrase cheap. An index consumes memory and maintenance time. A worker can avoid blocking the main thread but adds transfer, cancellation, and sequencing work. Effects have scheduling and lifecycle costs; an effect that repeatedly copies state can cause extra change detection and confusing error traces. Measure input delay, filter duration, count consistency, and allocated result size on a modest device. Prefer a bounded output event over storing the whole permitted case snapshot in browser storage.

Failure trace

Type a new phrase during a server snapshot replacement and assert rows and count agree in the same visible state. Read the computed rows twice without changing dependencies and inspect whether filtering ran only once. Replace case 62 with a new record and verify the new status appears; then deliberately mutate a nested object without signal update to expose the ownership rule. Block local storage and verify the queue still works. Mount and destroy the component while an effect owns a timer, then prove cleanup prevents a later write. Clear the phrase and check that only the server-authorized cases return.

Verification

  • Rows and count derive from the same source snapshot.
  • A computed getter has no external side effect.
  • Storage failure does not change authorized content.

Practice drill

Begin with 47 permit cases and grow to 47,000. Measure the filter and the DOM render separately. Replace the effect-driven visibleRows and visibleCount with two computed values that depend on the same source snapshot. Save only a small search preference, with a storage failure path. Insert a new permitted case while the reviewer types and record whether count and rows still agree. Add an intentional nested mutation as a negative test, then replace the snapshot correctly. Decide when the measured cost warrants an index or windowed list, including its memory budget.

Decision note

Keep source state writable, derived views computed, and effects restricted to work outside Angular state.

Common Mistakes

  • Copying one signal into another through an effect.
  • Mutating nested data without an explicit signal update.
  • Persisting a private case snapshot when only a display preference is needed.

Related lessons

Angular Signals and Delivery Boundaries; Angular Component Input, Output, and Row Identity; Angular HttpClient Streams and Stale Detail Requests; Angular SSR Hydration and Private Transfer State; Vue Source State, Computed Views, and Large Snapshots; Application Search and Retrieval.

Apply and check

Build Project: Angular permit operations queue and review Web Development: Angular signals and delivery quiz.

web-tech
web-development
Storage details