Group the product carousel into multi-item slides
Angular · Angular · intermediate · modification
Upgrades the product carousel from one card per slide to a grid of itemsPerSlide cards per slide. Adds an itemsPerSlide input (default 4) and a slides() computed that chunks the flat product list into fixed-size groups; the template and dot navigation now iterate slides() instead of the raw products. Verified against the demo collection of 12 products, which pages cleanly end to end.
A merchandising carousel on the storefront home page; collections are editor-curated and range from a few products to a few dozen, so the list length varies collection to collection.
Requirements
- The carousel groups the flat product list into fixed-size slides of itemsPerSlide (default 4) and shows one slide at a time, with a dot per slide plus Prev/Next controls.
- itemsPerSlide is a fixed positive integer (default 4), set by the host template as a layout constant; it is always >= 1 — never zero, negative, or fractional.
- products is arbitrary — whatever the catalog API returned for this collection — and is NOT guaranteed to be a multiple of itemsPerSlide.
- Every product must be reachable: each product has to appear on exactly one slide, and every slide must be reachable through the dots or Prev/Next.
- The product list is never empty; an empty collection is handled upstream and this component is not rendered.
Files touched
- src/app/storefront/product-carousel.component.ts
--- src/app/storefront/product-carousel.component.ts
-import { Component, input, signal } from '@angular/core';
+import { Component, computed, input, signal } from '@angular/core';
interface Product {
<div class="carousel">
<div class="slide">
- <article class="card">
- <h3>{{ products()[currentSlide()].name }}</h3>
- <p>{{ products()[currentSlide()].price }}</p>
- </article>
+ @for (product of slides()[currentSlide()]; track product.id) {
+ <article class="card">
+ <h3>{{ product.name }}</h3>
+ <p>{{ product.price }}</p>
+ </article>
+ }
</div>
<nav class="dots" aria-label="Carousel slides">
<button (click)="prev()" [disabled]="currentSlide() === 0">Prev</button>
- @for (product of products(); track product.id) {
+ @for (slide of slides(); track $index) {
<button
class="dot"
></button>
}
- <button (click)="next()" [disabled]="currentSlide() >= products().length - 1">
+ <button (click)="next()" [disabled]="currentSlide() >= slides().length - 1">
Next
</button>