I am new to Angular. I started with Angular 19 that were using zone.js for change detection. But then I moved to Angular 20 and now I am using zoneless. I have a put method call that I am trying to convert using Signals. Here is the code without using signal.
Cart.ts
export interface Cart {
id: number;
products: { productId: number }[];
}
CartService:
@Injectable({
providedIn: 'root'
})
export class CartService {
// cart property to keep a local cache of the user cart
cart: Cart | undefined;
addProduct(id: number): Observable<Cart> {
const cartProduct = { productId: id, quantity: 1 };
return defer(() =>
!this.cart
? this.httpClient.post<Cart>(this.fakeStoreCartUrl, { products: [cartProduct] })
: this.httpClient.put<Cart>(`${this.fakeStoreCartUrl}/${this.cart.id}`, {
products: [
...this.cart.products,
cartProduct
]
})
).pipe(map(cart => this.cart = cart));
}
}
then there is a guard which looks like:
export const checkoutGuard: CanDeactivateFn<CartComponent> = (component, currentRoute, currentState, nextState) => {
const dialog = inject(MatDialog);
const cartService = inject(CartService);
if (cartService.cart) {
const confirmation = dialog.open(
CheckoutComponent,
{ data: cartService.cart.products.length }
).afterClosed();
return confirmation;
}
return true;
};
Here is the route in which it is using.
export const routes: Routes = [
....
{
path: 'cart',
component: CartComponent,
canActivate: [authGuard],
canDeactivate: [checkoutGuard]
},
...
]
After converting to zoneless I noticed that this checkoutGuard is no longer working. I tried to convert it to using Signal so change detection work. But I am getting an error. Here is what I did:
export class CartService {
cart = signal<Cart | undefined>(undefined);
addProduct(id: number): Observable<Cart> {
const cartProduct = { productId: id, quantity: 1 };
return defer(() => // error
!this.cart
? this.httpClient.post<Cart>(this.fakeStoreCartUrl, { products: [cartProduct] })
: this.httpClient.put<Cart>(`${this.fakeStoreCartUrl}/${this.cart()?.id}`, {
products: [
...this.cart()?.products, // error
cartProduct
]
})
).pipe(map(cart => this.cart.set(cart)));
}
}
I also change the checkoutGuard.ts
export const checkoutGuard: CanDeactivateFn<CartComponent> = (component, currentRoute, currentState, nextState) => {
const dialog = inject(MatDialog);
const cartService = inject(CartService);
// Access a signal within the component to check for unsaved changes
if (cartService.cart()) {
const confirmation = dialog.open(
CheckoutComponent,
{ data: cartService.cart()?.products.length }
).afterClosed();
return confirmation;
}
return true;
};
But now I am getting errors at in CartService
...this.cart()?.products, --> Type '{ productId: number; }[] | undefined' must have a '[Symbol.iterator]()' method that returns an iterator.ts(2488)
At return
Type 'Observable<void>' is not assignable to type 'Observable<Cart>'. Type 'void' is not assignable to type 'Cart'.ts(2322)
I am checking ...this.cart()?.products, with Optional Chaining. All other places it is working but at this line I am getting error.
Also after setting value to signal. Error is return type is Observable instead of Observable. May be there is a better solution but as I said I am new to Angular so I want to understand the cause of error as well as if there is a better solution.
Thanks