Sometimes you inject a signal from a service, and it’s read-only, but you still want the user to edit it inside your component.
@Component({
selector: 'user-editor',
template: `
<input [(ngModel)]="?">
`
})
export class UserEditor {
readonly user = inject(UserService).userSignal; // Signal<User>
readonly username = computed(() => this.user().name); // Signal<string>
}
How do you make that happen without breaking immutability?
That’s where linkedSignal() comes in.
Linked Signal to the rescue
Linked Signal creates a local writable signal that stays in sync with the original one, so you can use it for two-way binding with the familiar banana-in-a-box syntax [(model)].
@Component({
selector: 'user-editor',
template: `
<input [(ngModel)]="editableUsername">
`
})
export class UserEditor {
readonly user = inject(UserService).userSignal; // Signal<User>
readonly username = computed(() => this.user().name); // Signal<string>
readonly editableUsername = linkedSignal(() => this.username());
}
- Keeps your parent state immutable
- Allows local edits in the child
- Feels natural for Angular developers
That’s the beauty of linkedSignal. It bridges Angular’s modern signal world with the old-school simplicity of banana-in-a-box syntax.
Value Source
Now the linked signal may hold a value that’s either coming from the service or modified locally by the user.
But how do you tell which one it is?
You can use a computed signal to compare the original value with the current value of the linked signal:
readonly valueSource = computed(() =>
this.username() === this.editableUsername()
? 'remote'
: 'local');
The valueSourcesignal now tells you whether the current value is local (changed by the user) or remote (still matches the service).
If you found this useful, follow me for more posts about Angular Signals, NgRx Signal Store, and building scalable Angular apps.
Top comments (0)