Add the checkout Place order button
Angular · Angular · beginner · greenfield
Adds the checkout submit action as a small standalone component. Placing an order flips the button label to a progress state, navigates to the confirmation page when the API resolves, and surfaces a retry message when it rejects. State is all signals, template uses the new control flow.
Checkout page of the storefront. Order creation charges the customer's saved payment method server-side.
Requirements
- Add the `CheckoutActionsComponent` for the checkout page: a Place order button that submits the current cart through `OrdersService.placeOrder()` and, on success, navigates to `/orders/{id}/confirmation`.
- `OrdersService.placeOrder()` sends `POST /api/orders`, which creates a NEW order from the cart on every call — the endpoint has no idempotency key. It resolves with `{ id: string }` and rejects with an `Error` on any failure.
- Exactly one order must be placed per checkout, no matter how the user clicks — including rapid double-clicks on the button while the request is still in flight.
- While the request is in flight the button reads `Placing order…` (it reads `Place order` otherwise).
- On failure, show the message `Couldn't place the order. Try again.` above the button and let the user retry.
- The confirmation route is a plain static route with no guards or resolvers; `router.navigate` to it always succeeds and resolves `true`. Treat navigation as infallible — only the `placeOrder()` call can reject.
Files touched
- src/app/checkout/checkout-actions.component.ts
--- src/app/checkout/checkout-actions.component.ts
+import { Component, inject, signal } from '@angular/core';
+import { Router } from '@angular/router';
+import { OrdersService } from '../services/orders.service';
+
+@Component({
+ selector: 'app-checkout-actions',
+ standalone: true,
+ template: `
+ <div class="checkout-actions">
+ @if (errorMessage()) {
+ <p class="checkout-error">{{ errorMessage() }}</p>
+ }
+ <button type="button" class="btn-primary" (click)="placeOrder()">
+ {{ placing() ? 'Placing order…' : 'Place order' }}
+ </button>
+ </div>
+ `,
+})
+export class CheckoutActionsComponent {
+ private readonly orders = inject(OrdersService);
+ private readonly router = inject(Router);
+
+ readonly placing = signal(false);
+ readonly errorMessage = signal('');
+
+ async placeOrder(): Promise<void> {
+ this.placing.set(true);
+ this.errorMessage.set('');
+ try {