File
Description
Generic carousel component that can be used to render any carousel items,
such as products, images, banners, or any component. Carousel items are
rendered in so-called carousel slides, and the previous/next buttons as well as
the indicator-buttons can used to navigate the slides.
The component uses an array of Observables (items$
) as an input, to allow
for lazy loading of items.
The number of items per slide is calculated with the itemWidth
, which can given
in pixels or percentage.
To allow for flexible rendering of items, the rendering is delegated to the
given template
. This allows for maximum flexibility.
Implements
Index
Properties
|
|
Methods
|
|
Inputs
|
|
Accessors
|
|
Constructor
constructor(el: ElementRef, service: CarouselService, changeDetectorRef: ChangeDetectorRef)
|
|
Parameters :
Name |
Type |
Optional |
el |
ElementRef
|
No
|
service |
CarouselService
|
No
|
changeDetectorRef |
ChangeDetectorRef
|
No
|
|
autocycleInterval
|
Type : number
|
Default value : 5000
|
|
autocycleOn
|
Type : boolean
|
Default value : true
|
|
Autocycle configuration
Autocycle on is default, interval 5000ms
|
hideIndicators
|
Type : boolean
|
Default value : false
|
|
Indicates whether the visual indicators are used.
|
hideNavigation
|
Type : boolean
|
Default value : false
|
|
indicatorIcon
|
Type : any
|
Default value : ICON_TYPE.CIRCLE
|
|
items
|
Type : Array<Observable<any>>
|
|
itemWidth
|
Type : string
|
Default value : '300px'
|
|
Specifies the minimum size of the carousel item, either in px or %.
This value is used for the calculation of numbers per carousel, so that
the number of carousel items is dynamic. The calculation uses the itemWidth
and the host element clientWidth , so that the carousel is reusable in
different layouts (for example in a 50% grid).
|
nextIcon
|
Type : any
|
Default value : ICON_TYPE.CARET_RIGHT
|
|
previousIcon
|
Type : any
|
Default value : ICON_TYPE.CARET_LEFT
|
|
|
template
|
Type : TemplateRef<any>
|
|
The template is rendered for each item, so that the actual
view can be given by the compoent that uses the CarouselComponent .
|
title
|
Type : string
|
|
The title is rendered as the carousel heading.
|
Methods
ngOnChanges
|
ngOnChanges()
|
|
|
ngOnDestroy
|
ngOnDestroy()
|
|
|
previousSlide
|
previousSlide()
|
|
|
autocycleIntervalId
|
Type : any
|
|
items
|
Type : Array<Observable<any>>
|
|
The items$ represent the carousel items. The items$ are
observables so that the items can be loaded on demand.
|
size
|
Type : number
|
Default value : 0
|
|
size$
|
Type : Observable<number>
|
|
Accessors
setItems
|
setsetItems(inputItems: Array>)
|
|
Parameters :
Name |
Type |
Optional |
inputItems |
Array<Observable<any>>
|
No
|
|
import {
ChangeDetectionStrategy,
Component,
ElementRef,
Input,
isDevMode,
OnInit,
TemplateRef,
OnDestroy,
ChangeDetectorRef,
OnChanges,
} from '@angular/core';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
import { ICON_TYPE } from '@spartacus/storefront';
import { CarouselService } from './carousel.service';
/**
* Generic carousel component that can be used to render any carousel items,
* such as products, images, banners, or any component. Carousel items are
* rendered in so-called carousel slides, and the previous/next buttons as well as
* the indicator-buttons can used to navigate the slides.
*
* The component uses an array of Observables (`items$`) as an input, to allow
* for lazy loading of items.
*
* The number of items per slide is calculated with the `itemWidth`, which can given
* in pixels or percentage.
*
* To allow for flexible rendering of items, the rendering is delegated to the
* given `template`. This allows for maximum flexibility.
*/
@Component({
selector: 'cx-carousel',
templateUrl: './carousel.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: false
})
export class CarouselComponent implements OnInit, OnDestroy, OnChanges {
/**
* The title is rendered as the carousel heading.
*/
@Input() title: string;
/**
* The items$ represent the carousel items. The items$ are
* observables so that the items can be loaded on demand.
*/
items: Array<Observable<any>>;
// eslint-disable-next-line @angular-eslint/no-input-rename
@Input('items')
set setItems(inputItems: Array<Observable<any>>) {
this.items = inputItems;
// Reset slider when changing products
this.activeSlide = 0;
}
/**
* The template is rendered for each item, so that the actual
* view can be given by the compoent that uses the `CarouselComponent`.
*/
@Input() template: TemplateRef<any>;
/**
* Specifies the minimum size of the carousel item, either in px or %.
* This value is used for the calculation of numbers per carousel, so that
* the number of carousel items is dynamic. The calculation uses the `itemWidth`
* and the host element `clientWidth`, so that the carousel is reusable in
* different layouts (for example in a 50% grid).
*/
@Input() itemWidth = '300px';
/**
* Indicates whether the visual indicators are used.
*/
@Input() hideIndicators = false;
@Input() indicatorIcon = ICON_TYPE.CIRCLE;
/**
* Navigation configuration
*/
@Input() previousIcon = ICON_TYPE.CARET_LEFT;
@Input() nextIcon = ICON_TYPE.CARET_RIGHT;
@Input() hideNavigation = false;
/**
* Autocycle configuration
* Autocycle on is default, interval 5000ms
*/
@Input() autocycleOn = true;
@Input() autocycleInterval = 5000;
activeSlide: number;
size$: Observable<number>;
autocycleIntervalId: any; // variable to clear autocycle interval
size = 0;
constructor(protected el: ElementRef, protected service: CarouselService, private changeDetectorRef: ChangeDetectorRef) {}
ngOnInit() {
if (!this.template && isDevMode()) {
console.error('No template reference provided to render the carousel items for the `cx-carousel`');
return;
}
this.size$ = this.service.getItemsPerSlide(this.el.nativeElement, this.itemWidth).pipe(
tap((calculatedSize: number) => {
this.activeSlide = 0;
this.size = calculatedSize;
})
);
}
ngOnChanges() {
clearInterval(this.autocycleIntervalId);
if (this.autocycleOn === true) {
this.autocycle();
}
}
nextSlide() {
if (this.activeSlide < this.items.length - 1) {
this.activeSlide += this.size;
} else this.activeSlide = 0;
}
previousSlide() {
if (this.activeSlide > 0) {
this.activeSlide -= this.size;
} else this.activeSlide = this.items.length - 1;
}
autocycle() {
this.autocycleIntervalId = setInterval(() => {
this.nextSlide();
this.changeDetectorRef.detectChanges();
}, this.autocycleInterval);
}
ngOnDestroy() {
clearInterval(this.autocycleIntervalId);
}
}
<!-- Copyright (c) 2018 SAP SE or an SAP affiliate company. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
**************************
This file was modified:
Modifications copyright (c) 2023 Crownpeak Technology GmbH
Original source from: https://github.com/SAP/spartacus/blob/develop/projects/storefrontlib/src/shared/components/carousel/carousel.component.html (commit 58f74b8 on 19 May 2020)
Modifications:
* moved logic for previous and next buttons to spartacusCarousel.component.ts
-->
<ng-container *ngIf="items?.length > 0 && (size$ | async) as size">
<h3 *ngIf="title">{{ title }}</h3>
<div class="carousel-panel" [ngClass]="'size-' + size">
<button
*ngIf="hideNavigation === false && size < items.length && autocycleOn === false"
class="previous"
(click)="previousSlide()"
[disabled]="activeSlide === 0"
>
<cx-icon [type]="previousIcon"></cx-icon>
</button>
<div class="slides">
<ng-container *ngFor="let _ of items; let i = index">
<div class="slide" *ngIf="i % size === 0" [class.active]="i === activeSlide">
<ng-container *ngFor="let item of items | slice: i:i + size; let j = index">
<div *ngIf="item | async as data" class="item" [class.active]="i === activeSlide">
<ng-container *ngTemplateOutlet="template; context: { item: data }"></ng-container>
</div>
</ng-container>
</div>
</ng-container>
</div>
<button
*ngIf="hideNavigation === false && size < items.length && autocycleOn === false"
class="next"
(click)="nextSlide()"
tabindex="-1"
[disabled]="activeSlide > items.length - size - 1"
>
<cx-icon [type]="nextIcon"></cx-icon>
</button>
</div>
<div *ngIf="!hideIndicators && size < items.length" class="indicators">
<ng-container *ngFor="let _ of items; let i = index">
<button *ngIf="i % size === 0" (focus)="activeSlide = i" [disabled]="i === activeSlide" tabindex="-1">
<cx-icon [type]="indicatorIcon"></cx-icon>
</button>
</ng-container>
</div>
</ng-container>
Legend
Html element with directive