This tutorial Update an Angular Signal's Value and Make Computed Signal Emit Updates really helped me work on this problem.
Key points to keep in mind:
- Use the set method for updating primitive values like strings or numbers
- Use the update method with a callback that returns a new object or array when updating complex values
- Avoid mutating signal values directly
So, we can use update when updating complex objects inside a signal and the Html will be updated with the changes. When working with simple primitives, set method is enough to notify the change.
After much effort, the update method can be structured like so.
from component it will be
updateCartItem(enrollment: Enrollment, name: string, price: number) {
enrollment.setName(name);
enrollment.setPrice(price);
enrollment.setEdit(false);
this.cart.updateCartItem(enrollment);
}
THen the class can be used to update the signal list using update
public updateCartItem(enrollment: Enrollment) {
this._enrollments.update((enrollments: Enrollment[]) => {
const foundEnrollmentIndex: number = enrollments.findIndex(
(item: any) => item.productId === enrollment.productId()
);
enrollments[foundEnrollmentIndex] = enrollment;
return enrollments;
});
}
Full Code:
import { Component, signal } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { bootstrapApplication } from '@angular/platform-browser';
import 'zone.js';
export class Enrollment {
private _productId = signal(0);
private _price = signal(0);
private _name = signal('');
private _edit = signal(false);
public productId = this._productId.asReadonly();
public price = this._price.asReadonly();
public name = this._name.asReadonly();
public edit = this._edit.asReadonly();
public setProductId(productId: number): void {
this._productId.set(productId);
}
public setPrice(price: number): void {
this._price.set(price);
}
public setName(name: string): void {
this._name.set(name);
}
public setEdit(edit: boolean): void {
this._edit.set(edit);
}
}
export class Cart {
private _enrollments = signal<Enrollment[]>([]);
public enrollments = this._enrollments.asReadonly();
public addToCart(enrollment: Enrollment): void {
this._enrollments.update((prev: Enrollment[]) => [...prev, enrollment]);
}
public removeFromCart(enrollment: Enrollment): void {
this._enrollments.update((prev: Enrollment[]) =>
this._enrollments().filter((e) => e !== enrollment)
);
}
public emptyCart(): void {
this._enrollments.set([]);
}
public updateCartItem(enrollment: Enrollment) {
this._enrollments.update((enrollments: Enrollment[]) => {
const foundEnrollmentIndex: number = enrollments.findIndex(
(item: any) => item.productId === enrollment.productId()
);
enrollments[foundEnrollmentIndex] = enrollment;
return enrollments;
});
}
}
@Component({
selector: 'app-root',
standalone: true,
imports: [FormsModule],
template: `
<form>
<div><input #name/></div>
<div><input #price type="number"/></div>
<div><button (click)="addToCart($any(name).value, $any(price).value)">Add New Item</button></div>
</form>
@for(enrollment of cart.enrollments(); track $index) {
<div>
@if(!enrollment.edit()) {
productId: {{enrollment.productId()}} | name: {{enrollment.name()}} | price: {{enrollment.price()}}$
<button (click)="editEnrollment(enrollment)">edit</button>
<button (click)="removeFromCart(enrollment)">remove</button>
} @else {
<div><input [value]="enrollment.name()" #nameEdit/>
<input [value]="enrollment.price()" #priceEdit type="number"/></div>
<button (click)="updateCartItem(enrollment, nameEdit.value, +priceEdit.value)">update</button>
}
</div>
}
`,
})
export class App {
cart = new Cart();
name = '';
price = 0;
removeFromCart(enrollment: Enrollment) {
this.cart.removeFromCart(enrollment);
}
addToCart(name: string, price: number) {
console.log(name, price);
if (!name && !price) {
alert('name and price needed');
return;
}
const newEnrollment = new Enrollment();
newEnrollment.setProductId(Math.round(Math.random() * 10000));
newEnrollment.setName(name);
newEnrollment.setPrice(price);
this.cart.addToCart(newEnrollment);
this.name = '';
this.price = 0;
}
editEnrollment(enrollment: Enrollment) {
enrollment.setEdit(true);
this.cart.updateCartItem(enrollment);
}
updateCartItem(enrollment: Enrollment, name: string, price: number) {
enrollment.setName(name);
enrollment.setPrice(price);
enrollment.setEdit(false);
this.cart.updateCartItem(enrollment);
}
}
bootstrapApplication(App);