A linkedSignal is a type of signal that stays connected to another value. Unlike a regular signal, which might become outdated when its dependencies change, linkedSignal always updates itself when needed.
Why Use linkedSignal?
Let’s say you are making a fantasy RPG character creator. Players can pick a character class (like Warrior, Mage, or Rogue), and each class comes with a default weapon.
The Old Way Using Regular Signals
1@Component({2 /* ... */3})4export class CharacterCreator {5 availableClasses = signal<string[]>(['Warrior', 'Mage', 'Rogue']);6 defaultWeapons = signal<Record<string, string>>({7 Warrior: 'Sword',8 Mage: 'Staff',9 Rogue: 'Dagger',10 });11
12 selectedClass = signal(this.availableClasses()[0]);13 selectedWeapon = signal(this.defaultWeapons()[this.selectedClass()]);14
15 changeClass(newClassIndex: number) {8 collapsed lines
16 this.selectedClass.set(this.availableClasses()[newClassIndex]);17 this.selectedWeapon.set(this.defaultWeapons()[this.selectedClass()]);18 }19
20 removeWarriorClass() {21 this.availableClasses.set(['Mage', 'Rogue')22 }23}This setup has a problem. If availableClasses changes, selectedClass could point to a class that no longer exists. Also, selectedWeapon does not update automatically when selectedClass changes.
The Better Way with linkedSignal
Using linkedSignal, we make sure selectedWeapon always updates when selectedClass changes.
1@Component({2 /* ... */3})4export class CharacterCreator {5 availableClasses = signal<string[]>(['Warrior', 'Mage', 'Rogue']);6 defaultWeapons = signal<Record<string, string>>({7 Warrior: 'Sword',8 Mage: 'Staff',9 Rogue: 'Dagger',10 });11
12 selectedClass = linkedSignal(() => this.availableClasses()[0]);13 selectedWeapon = linkedSignal(() => this.defaultWeapons()[this.selectedClass()]);14
15 changeClass(newClassIndex: number) {7 collapsed lines
16 this.selectedClass.set(this.availableClasses()[newClassIndex]);17 }18
19 removeWarriorClass() {20 this.availableClasses.set(['Mage', 'Rogue')21 }22}With linkedSignal:
selectedClassupdates to the first class if the class list changes.selectedWeaponautomatically updates whenselectedClasschanges.
Keeping the User’s Selection
Sometimes, we want to keep the user’s selection if it is still valid. If the class list changes but still includes the chosen class, we don’t want to reset it.
1@Component({2 /* ... */3})4export class CharacterCreator {5 availableClasses = signal<string[]>(['Warrior', 'Mage', 'Rogue']);6 defaultWeapons = signal<Record<string, string>>({7 Warrior: 'Sword',8 Mage: 'Staff',9 Rogue: 'Dagger',10 });11
12 selectedClass = linkedSignal<string[], string>({13 source: this.availableClasses,14 computation: (newClasses, previous) =>15 previous && newClasses.includes(previous.value) ? previous.value : newClasses[0],4 collapsed lines
16 });17
18 selectedWeapon = linkedSignal(() => this.defaultWeapons()[this.selectedClass()]);19}Now, if the selected class still exists after an update, it remains selected. If it is removed, the first class in the new list is selected.
Custom Comparison for Updates
By default, linkedSignal updates whenever its source changes. However, sometimes we only want to update when a key part of the data changes.
For example, in an NPC editor, we may consider an NPC the same if their ID stays the same, even if other details change.
1const activeNPC = signal({ id: 1, name: 'Eldrin', role: 'Merchant' });2
3const npcBackup = linkedSignal(() => this.activeNPC(), {4 equal: (a, b) => a.id === b.id,5});This way, npcBackup only updates when a new NPC is assigned, not when only their details change.
Conclusion
linkedSignal is a useful tool for managing state in Angular. It helps keep data in sync and reduces bugs. Whether you are building a character creator, an NPC editor, or any app with linked data, linkedSignal makes state management easier.