Files
ECC/pi/core/skills/angular-developer/references/signals-overview.md
T
Affaan MustafaandGitHub c70874fae9 feat(pi): curated pi/core skills+prompts profile, CI load test, and 2.2.2 release sync (#3264)
Adds a curated, Pi-native, skills+prompts-only profile at pi/core/ for downstream packagers that mirror GitHub Releases.

- manifests/pi-core.json: explicit include lists, per-item exclusion reasons, curation rules, safety allowlists; every root skill and command must be classified.
- scripts/build-pi-core.js regenerates pi/core deterministically (package.json from VERSION, LICENSE, README.md, CURATION.md, skills/, commands/); --check fails CI on drift. council is renamed ecc-council inside pi/core only.
- Safety checks: no callable endpoints outside the allowlist, no npx/curl|sh/pip install, no secrets, no absolute home paths, no symlinks, valid frontmatter, no duplicate names.
- CI: build + drift check and an offline Pi CLI load test of pi/core.
- Release: VERSION, package.json and pi/core/package.json at 2.2.2, CHANGELOG, tag-triggered release verification, and a two-week cadence in CONTRIBUTING.md.

pi/core: 123 of 293 skills and 24 of 94 commands; 35,006 characters of skill description text.
2026-09-29 20:24:05 -05:00

2.8 KiB

Angular Signals Overview

Signals are the foundation of reactivity in modern Angular applications. A signal is a wrapper around a value that notifies interested consumers when that value changes.

Writable Signals (signal)

Use signal() to create state that can be directly updated.

import {signal} from '@angular/core';

// Create a writable signal
const count = signal(0);

// Read the value (always requires calling the getter function)
console.log(count());

// Update the value directly
count.set(3);

// Update based on the previous value
count.update((value) => value + 1);

Exposing as Readonly

When exposing state from a service, it is a best practice to expose a readonly version to prevent external mutation.

private readonly _count = signal(0);
// Consumers can read this, but cannot call .set() or .update()
readonly count = this._count.asReadonly();

Computed Signals (computed)

Use computed() to create read-only signals that derive their value from other signals.

  • Lazily Evaluated: The derivation function doesn't run until the computed signal is read.
  • Memoized: The result is cached. It only recalculates when one of the signals it depends on changes.
  • Dynamic Dependencies: Only the signals actually read during the derivation are tracked.
import {signal, computed} from '@angular/core';

const count = signal(0);
const doubleCount = computed(() => count() * 2);

// doubleCount automatically updates when count changes.

Reactive Contexts

A reactive context is a runtime state where Angular monitors signal reads to establish a dependency.

Angular automatically enters a reactive context when evaluating:

  • computed signals
  • effect callbacks
  • linkedSignal computations
  • Component templates

Untracked Reads (untracked)

If you need to read a signal inside a reactive context without creating a dependency (so that the context doesn't re-run when the signal changes), use untracked().

import {effect, untracked} from '@angular/core';

effect(() => {
  // This effect only runs when currentUser changes.
  // It does NOT run when counter changes, even though counter is read here.
  console.log(`User: ${currentUser()}, Count: ${untracked(counter)}`);
});

Async Operations in Reactive Contexts

The reactive context is only active for synchronous code. Signal reads after an await will not be tracked. Always read signals before asynchronous boundaries.

// Incorrect: theme() is not tracked because it is read after await
effect(async () => {
  const data = await fetchUserData();
  console.log(theme());
});

// Correct: Read the signal before the await
effect(async () => {
  const currentTheme = theme();
  const data = await fetchUserData();
  console.log(currentTheme);
});