Book Detail — Facts
Overview
Standalone BookDetailComponent (route /books/:id). Shows one book and an Add to cart button.
Renders a “not found” branch when the id doesn’t match.
Component tree
Single component, no children. Uses RouterLink (back link + cart link) + CurrencyPipe. Template is
one @if (book(); as b) { … } @else { not-found }.
Data flow
:idarrives as a componentinput.required<string>()— enabled bywithComponentInputBinding()inapp.config.ts. Gotcha G below if that provider is removed.bookis acomputed()=BookService.getById(id()); returnsundefined→ the@elsenot-found branch.- Add to cart →
CartService.add(book)(the same shared singleton the list + cart pages use).
Routes & params
/books/:id — id is the Book.id slug (e.g. dune, clean-code). Unknown id → not-found view (no redirect).
Key files
| Role | File |
|---|---|
| Component | app/src/app/pages/book-detail/book-detail.component.ts |
| Template | …/book-detail.component.html |
| Styles | …/book-detail.component.scss |
| Data | app/src/app/services/book.service.ts |
| Cart state | app/src/app/services/cart.service.ts |
| Model | app/src/app/models/book.ts |
Gotchas
- Gotcha G — route-param binding:
idis read viainput.required(), which only works becauseprovideRouter(routes, withComponentInputBinding())is set. Remove that andid()throws / never binds. getByIdreturnsundefinedfor a bad id — the template must guard (@if … @else); don’t assume a book.- Shares
CartService— adding here updates the cart badge on every page.
Related
[[book-list]] (links here) · [[cart-checkout]] (consumes what Add-to-cart writes).