I'm trying to implement two-way binding in Angular18 by binding a property of an object within a signal (model) to ngModel. Specifically, I want to use a model signal as the source and ensure that changes update the corresponding property of the object and trigger signal notification cycle. However, the Angular documentation primarily shows examples with non-object signal values, which has left me confused about how to correctly modify object values.
Here are the approaches I've considered:
<input [(ngModel)]="personData().firstName"/>
<input [(ngModel)]="personData().lastName"/>
personData = model.required<personData>();
<input [ngModel]="personData().firstName"
(ngModelChange)="update($event)"/>
update($event: string | undefined) {
this.personData.update(value => ({...value, firstName: $event}));
}
So, my question is - is there another way of doing it so that the value would update and the update would notify consumers? I would appreciate any suggestions on best practices for this usecase.
In your example, the two-way binding directly mutates the values inside signals without notifying the signal, which can cause issues where updates are not reflected correctly.
Signal works fine with [(ngModel)]="regularSignal"
so there is no need for model input
Instead of passing a primitive value from the parent, you can pass an object containing signals, as shown below:
export class PersonalComponent {
personData = input.required<{
firstName: Signal<string>;
lastName: Signal<string>;
}>();
_ = effect(() => {
console.log(`[PERSONAL] Component ${this.personData().firstName()}`);
});
}
ParentComponent
@Component({
selector: 'app-root',
standalone: true,
imports: [PersonalComponent],
template: `
<app-personal [personData]="pData" />
`,
})
export class App {
pData = { firstName: signal('ng'), lastName: signal('dev') };
_ = effect(() => {
console.log(`[APP] Component ${this.pData.firstName()}`);
});
}