Files
ECC/pi/core/skills/angular-developer/references/linked-signal.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.5 KiB

Dependent State with linkedSignal

The linkedSignal function lets you create writable state that is intrinsically linked to some other state. It is perfect for state that needs a default value derived from an input or another signal, but can still be independently modified by the user.

If the source state changes, the linkedSignal resets to a new computed value.

Basic Usage

When you only need to recompute based on a source, pass a computation function. linkedSignal works like computed, but the resulting signal is writable (you can call .set() or .update() on it).

import { Component, signal, linkedSignal } from '@angular/core';

@Component({...})
export class ShippingMethodPicker {
  shippingOptions = signal(['Ground', 'Air', 'Sea']);

  // Defaults to the first option.
  // If shippingOptions changes, selectedOption resets to the new first option.
  selectedOption = linkedSignal(() => this.shippingOptions()[0]);

  changeShipping(index: number) {
    // We can still manually update this signal!
    this.selectedOption.set(this.shippingOptions()[index]);
  }
}

Advanced Usage: Accounting for Previous State

Sometimes, when the source state changes, you want to preserve the user's manual selection if it is still valid. To do this, use the object syntax providing source and computation.

The computation function receives the new value of the source, and a previous object containing the previous source value and the previous linkedSignal value.

interface ShippingMethod { id: number; name: string; }

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

  selectedOption = linkedSignal<ShippingMethod[], ShippingMethod>({
    source: this.shippingOptions,
    computation: (newOptions, previous) => {
      // If the newly loaded options still contain the user's previously
      // selected option, keep it selected. Otherwise, reset to the first option.
      return newOptions.find(opt => opt.id === previous?.value.id) ?? newOptions[0];
    }
  });
}

When to use linkedSignal vs computed vs effect

  • Use computed: When state is strictly derived from other state and should never be manually updated.
  • Use linkedSignal: When state is derived from other state, but the user must be able to override or manually update it.
  • Never use effect to sync one piece of state to another. That is an anti-pattern. Use computed or linkedSignal instead.