Reputation: 8605
I'm actually migrating to Angular 15 and saw that swiper 9 was out.
It's written that a simple npm i swiper
and that it should work, since
Custom elements are supported in all major browser and by almost every framework.
But I'm a bit lost since I cannot import it in the module anymore
Does somebody knows how to use the latest v9.0.0^
swiper version with angular ?
Upvotes: 25
Views: 35291
Reputation: 11
wanted to leave here another approach based on Just Pilot answer and this great answer about wrapping child components to isolated the swiper dependency to a component in the project.
slider.component.ts
import { AfterViewInit, Component, ContentChildren, CUSTOM_ELEMENTS_SCHEMA, effect, ElementRef, input, output, QueryList, ViewChild } from '@angular/core';
import { SlideDirective } from './slide.directive';
import { CommonModule } from '@angular/common';
const swiperOpts = {
slidesPerView: 1,
allowTouchMove: false,
scrollbar: true
}
@Component({
standalone: true,
selector: 'umu-slider',
templateUrl: './slider.component.html',
imports: [CommonModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class SliderComponent implements AfterViewInit {
// Optional external control of the slider
currentSlide = input<number>(0)
slideOpts = input<any>(swiperOpts)
onSlideChange = output<number>()
_currentSlide = 0
@ViewChild('slider') slider: ElementRef | undefined;
@ContentChildren(SlideDirective) slideItems: QueryList<SlideDirective>;
constructor() {
effect(() => {
this.goToSlide(this.currentSlide())
})
}
ngAfterViewInit(): void {
Object.assign(this.slider.nativeElement,this.slideOpts())
this.slider.nativeElement.initialize()
this.slider.nativeElement.addEventListener('swiperslidechange', (event) => {
this.onSlideChange.emit(event.detail[0].activeIndex)
})
}
goToSlide(slide: number) {
// If mother component has updated the input due to inner update ignore
if(slide == this._currentSlide) {
return
}
this._currentSlide = slide
this.slider.nativeElement.swiper.slideTo(slide);
}
}
slider.component.html
<swiper-container init="false" #slider>
@for(slide of slideItems; track $index){
<swiper-slide>
<ng-container [ngTemplateOutlet]="slide.slideTemplate"></ng-container>
</swiper-slide>
}
</swiper-container>
slide.directive.html
import { Directive, TemplateRef } from '@angular/core';
@Directive({
selector: '[appSlide]'
})
export class SlideDirective {
slideTemplate: TemplateRef<any>;
constructor(private templateRef: TemplateRef<any>) {
this.slideTemplate = this.templateRef;
}
}
example.page.html
<app-slider
[currentSlide]="currentSlide"
(onSlideChange)="changeSlide($event)"
>
<div *appSlide>
<h2>Slide 1</h2>
</div>
<div *appSlide>
<h2>Slide 2</h2>
</div>
<div *appSlide>
<h2>Slide 3</h2>
</div>
</app-slider>
Remember to do the proper imports of components and the directive as required in your project.
Upvotes: 0
Reputation: 138
With latest doc (v11) and without [CUSTOM_ELEMENTS_SCHEMA] or querySelector :
HTML:
<div class="swiper">
<div class="swiper-wrapper">
@for (slide of slides(); track slide) {
<div class="swiper-slide">
Ts:
import Swiper from 'swiper';
import { SwiperOptions } from 'swiper/types/swiper-options';
protected swiper = signal<Swiper>(undefined);
swiperParams: SwiperOptions = {
centeredSlides: true,
slidesPerView: 'auto',
zoom: true,
};
ngAfterViewInit(): void {
this.swiper.set(
new Swiper('.swiper', {
...this.swiperParams,
initialSlide: 1,
})
);
this.swiper().on('slideChange', () => {
// do something
});
}
global.scss :
@import '~swiper/swiper-bundle.css';
Upvotes: 1
Reputation: 11
If you are using Angular Routing Module (not apply to standalone components or modules). We need to do those implementations:
In app.component.ts import and change:
import {register} from 'swiper/element/bundle';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
public ngOnInit() {
register();
}
}
You may create a directive: swiper.directive.ts
import {Directive, ElementRef, Input, OnInit} from '@angular/core';
import {SwiperOptions} from 'swiper/types';
@Directive({
selector: '[directiveSwiper]'
})
export class SwiperDirective implements OnInit {
private readonly swiperElement: HTMLElement;
//Se obtiene la configuracion del HTML
@Input('config') config?: SwiperOptions;
public constructor(private el: ElementRef<HTMLElement>) {
this.swiperElement = el.nativeElement;
}
public ngOnInit() {
Object.assign(this.swiperElement, this.config);
this.swiperElement.initialize();
}
}
After that, you have to import it into the component module where you are going to use it, in this example we use example.module.ts, and then we need to add a schema metadata called CUSTOM_ELEMENTS_SCHEMA, so that Angular recognizes the swiper-container html tag:
import {SwiperDirective} from './swiper.directive';
@NgModule({
declarations: [ExampleComponent, SwiperDirective],
imports: [
CommonModule,
ExampleRoutingModule
],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class ExampleModule {}
In the component example.component.ts, let's configure the Swiper options:
import {Component, ElementRef, OnInit, ViewChild} from '@angular/core';
import {Swiper, SwiperOptions} from 'swiper/types';
@Component({
selector: 'app-example',
templateUrl: './example.component.html',
styleUrls: ['./example.component.scss']
})
export class ExampleComponent implements OnInit {
public config: SwiperOptions = {
slidesPerView: 1,
spaceBetween: 30,
navigation: true
};
public ngOnInit(): void {}
}
Finally, we need to define into example.component.html the swiper-component tag, with the directive directiveSwiper and Swiper options. It's important to implement init=false into swiper-container to be able to apply correctly the Swiper options and initialize Swiper from directive:
<swiper-container directiveSwiper [config]="config" class="mySwiper" init="false">
<swiper-slide>
<h1>Slide 1</h1>
</swiper-slide>
<swiper-slide>
<h1>Slide 2</h1>
</swiper-slide>
<swiper-slide>
<h1>Slide 3</h1>
</swiper-slide>
</swiper-container>
That's all, folks.
Upvotes: 1
Reputation: 161
Add the CUSTOM_ELEMENTS_SCHEMA to the @NgModule decorator for the AppModule. This ensures that Angular compiles and ignores the unknown swiper-container and swiper-slide custom elements and compiles without errors.
import {CUSTOM_ELEMENTS_SCHEMA, NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {AppRoutingModule} from './app-routing.module';
import {AppComponent} from './app.component';
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule, AppRoutingModule],
providers: [],
bootstrap: [AppComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class AppModule {}
It then becomes possible to add the swiper-container and swiper-slide elements to the template, however the code won't do much. The initialize the slide we need to call the register function as described in the documentation. If you look into to the source code Swiper, it shows that this function requires the DOM to be initialized. This means that you should place it in the component and in the ngAfterViewInit lifecycle hook. This gives us the following code (please note that the parameters can be passed in kebab-case):
import {AfterViewInit, Component, ElementRef, ViewChild} from '@angular/core';
import {register} from 'swiper/element/bundle';
@Component({
selector: 'app-root',
template: `
<swiper-container initial-slide="0" slides-per-view="1">
<swiper-slide>
<h1>Slide 1</h1>
</swiper-slide>
<swiper-slide>
<h1>Slide 2</h1>
</swiper-slide>
<swiper-slide>
<h1>Slide 3</h1>
</swiper-slide>
</swiper-container>
`,
})
export class AppComponent implements AfterViewInit {
ngAfterViewInit(): void {
register();
}
}
Access to the Swiper instance can be achieved through the ViewChild decorator. To demonstrate this, I've included a function that prints the index of the current slide to the console each time that the slide changes. Not that all the events described in the documentation are available but must be written in lowercase.
import {AfterViewInit, Component, ElementRef, ViewChild} from '@angular/core';
import {register} from 'swiper/element/bundle';
import {Swiper} from 'swiper/types';
@Component({
selector: 'app-root',
template: `
<swiper-container #swiperRef initial-slide="0" (swiperactiveindexchange)="onActiveIndexChange()" slides-per-view="1">
<swiper-slide>
<h1>Slide 1</h1>
</swiper-slide>
<swiper-slide>
<h1>Slide 2</h1>
</swiper-slide>
<swiper-slide>
<h1>Slide 3</h1>
</swiper-slide>
</swiper-container>
`,
})
export class AppComponent implements AfterViewInit {
@ViewChild('swiperRef')
swiperRef: ElementRef | undefined;
swiper?: Swiper;
ngAfterViewInit(): void {
register();
this.swiper = this.swiperRef?.nativeElement.swiper;
}
onActiveIndexChange() {
console.log(this.swiper?.activeIndex);
}
}
Upvotes: 16
Reputation: 1630
Although importing Swiper from an external CDN may work in some cases (together with the CUSTOM_ELEMENTS_SCHEMA
), you probably won't go too far if you need a more customized solution inside Angular.
I had a lot of headache when trying to create an interactive thumbnail gallery so I took my time to experiment all the suggested settings and tweaks (the accepted answer helped me a lot! Thanks!!). And I finally made Swiper 10 work inside an Angular 16 project!
All the source code is published at StackBlitz:
Sample thumbnail gallery (w/ source code)
For a better explanation of the whole setup please refer to the Medium publication that I made for this:
Swiper 10 & Angular 16 Uncovered (Medium article)
Upvotes: 0
Reputation: 41
I followed first answer
but had to change ngAfterViewInit() so that swiper initalization runs outside ngZone. Otherwise I'm getting NG0506 error
I use SSR
type SwiperHtmlElement = HTMLElement & { initialize: () => void }
constructor(private el: ElementRef, private ngZone: NgZone) {
this.swiperElement = el.nativeElement as SwiperHtmlElement;
}
ngAfterViewInit() {
Object.assign(this.swiperElement, this.config);
this.ngZone.runOutsideAngular(() => {
setTimeout(() => {
this.swiperElement.initialize();
});
})
}
Upvotes: 1
Reputation: 46
Create a Directive ng generate directive SwiperDirective
import {AfterViewInit, Directive, ElementRef, Input} from '@angular/core';
import {SwiperOptions} from "swiper";
@Directive({
selector: '[appSwiperDirective]'
})
export class SwiperDirectiveDirective implements AfterViewInit{
swiperElement: HTMLElement;
@Input('config')
config?: SwiperOptions;
constructor(private el: ElementRef<HTMLElement>) {
this.swiperElement = el.nativeElement;
}
ngAfterViewInit() {
Object.assign(this.el.nativeElement, this.config);
}
}
Add schemas: [CUSTOM_ELEMENTS_SCHEMA] and import Directive in your App Module
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
import { SharedModule } from './shared.module';
import { SwiperDirectiveDirective } from './swiper-directive.directive';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
@NgModule({
declarations: [
AppComponent,
SwiperDirectiveDirective
],
imports: [
BrowserModule,
AppRoutingModule,
HttpClientModule,
FontAwesomeModule,
SharedModule,
BrowserAnimationsModule
],
providers: [],
bootstrap: [AppComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class AppModule {
}
Add register();
Globally in app.component.ts file
import { Component, OnInit, AfterViewInit } from '@angular/core';
import { register } from 'swiper/element/bundle';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit, AfterViewInit {
constructor() { }
ngOnInit(): void {
}
ngAfterViewInit(): void {
register();
}
}
enter code here
Set your Swiper configuration in your component.ts file here
import { Component } from '@angular/core';
import { A11y, Mousewheel, SwiperOptions } from 'swiper';
@Component({
selector: 'app-partner-area',
templateUrl: './partner-area.component.html',
styleUrls: ['./partner-area.component.scss'],
})
export class PartnerAreaComponent {
public config: SwiperOptions = {
modules: [ A11y, Mousewheel],
autoHeight: true,
spaceBetween: 20,
slidesPerView: 3,
breakpoints: {
1200: {
slidesPerView: 6,
centeredSlides: false
},
}
}
}
Add the HTML file
<section class="partner-area ptb-100">
<swiper-container appSwiperDirective [config]="config" class="container">
<swiper-slide class="single-item">
<div class="partner-area__single">
<img src="https://picsum.photos/500" alt="partner">
</div>
</swiper-slide>
<swiper-slide class="single-item">
<div class="partner-area__single">
<img src="https://picsum.photos/500" alt="partner">
</div>
</swiper-slide>
</swiper-container>
</section>
Upvotes: 2
Reputation: 8605
Although I've accepted this answer as being the solution, I actually had to do the following. Which takes a bunch of Ideas I've red & found over the swiper.js doc
This is the most optimized & cleaner version that I could find.
import { register } from 'swiper/element/bundle'
constructor() {
register()
}
register()
should be called only once so the app.component.ts
is the place to go.<swiper-container
#swiperRef
init="false"
>
<swiper-slide *ngFor="let i of items; trackBy: trackByFn">
<!-- slide -->
</swiper-slide>
</swiper-container>
init="false"
will let you prepare the options, if you wish it to. Will also let you subscribe on changes like slideChange
.import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'
@NgModule({
// ...
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
Note: you have to declare it in the component that will be using the swiper element Do not declare it globally
@ViewChild('swiperRef', { static: true })
protected _swiperRef: ElementRef | undefined
swiper?: Swiper
ngOnInit() {
this._initSwiper()
}
private _initSwiper() {
const options: SwiperOptions = {
pagination: { clickable: true },
slidesPerView: 1,
breakpoints: this._getBreakpoints(), // In case you wish to calculate base on the `items` length
}
const swiperEl = this._swiperRef.nativeElement
Object.assign(swiperEl, options)
swiperEl.initialize()
if (this.swiper) this.swiper.currentBreakpoint = false // Breakpoint fixes
this.swiper = this._swiperRef.nativeElement.swiper
this.swiper.off('slideChange') // Avoid multiple subscription, in case you wish to call the `_initSwiper()` multiple time
this.swiper.on('slideChange', () => { // Any change subscription you wish
this.infinitLoad?.triggerOnScroll()
})
}
Note: the swiper-container
shall not be in an *ngIf
. if it is, change the following in the .component.ts
file.
@ViewChild('swiperRef', { static: false }) // Change to "false"
protected _swiperRef: ElementRef | undefined
itemsLoaded() { // Shall not be used in `ngOnInit` because it isn't rendered yet
this._initSwiper()
}
swiper-container
to avoid using *ngIfUpvotes: 6
Reputation: 79
swiper9 made it simple to use just you have to mention "schemas: [CUSTOM_ELEMENTS_SCHEMA],"
in your module.ts file where your component is imported
//below steps are for module.ts file
// 1 step :
import { CUSTOM_ELEMENTS_SCHEMA,} from '@angular/core';
// 2nd step:
mention "schemas: [CUSTOM_ELEMENTS_SCHEMA]," inside @NgModule
// 3rd step :
import { register } from 'swiper/element/bundle';
// and
register();
now your code works as expected "add below code is in html file"
<swiper-container #swiper initial-slide="0">
<swiper-slide>Slide 1</swiper-slide>
<swiper-slide>Slide 2</swiper-slide>
<swiper-slide>Slide 3</swiper-slide>
<swiper-slide>Slide 4</swiper-slide>
<swiper-slide>Slide 5</swiper-slide>
<swiper-slide>Slide 6</swiper-slide>
<swiper-slide>Slide 7</swiper-slide>
<swiper-slide>Slide 8</swiper-slide>
<swiper-slide>Slide 9</swiper-slide>
</swiper-container>
without any errors
Upvotes: 7
Reputation: 111
In the case you want to initialize manually (with init="false"), you can do so using these typings.
in themplate.html
<swiper-container init="false" class="pager" #swiper>
<swiper-slide *ngFor="[...]">
</swiper-slide>
</swiper-container>
in component
@Component({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
// [...]
})
export class NewsPagerComponent implements AfterViewInit, OnInit {
@ViewChild('swiper') swiperRef: ElementRef<HTMLElement & { swiper?: Swiper } & { initialize: () => void }> | undefined;
swiper?: Swiper;
ngOnInit(): void {
register();
}
ngAfterViewInit(): void {
const swiperEl = Object.assign(this.swiperRef.nativeElement, {
modules: [Navigation],
navigation: true
});
swiperEl.initialize();
this.swiper = this.swiperRef.nativeElement.swiper;
}
}
And now you have the tools to follow the official docs of swiper-element for anything else.
Upvotes: 2
Reputation: 624
In AppModule add:
import {register} from 'swiper/element/bundle';
register();
Create a Directive
import {AfterViewInit, Directive, ElementRef, Input} from '@angular/core';
import {SwiperOptions} from "swiper";
@Directive({
selector: '[fmSwiper]',
standalone: true,
})
export class SwiperDirective implements AfterViewInit {
private readonly swiperElement: HTMLElement;
@Input('config')
config?: SwiperOptions;
constructor(private el: ElementRef<HTMLElement>) {
this.swiperElement = el.nativeElement;
}
ngAfterViewInit() {
Object.assign(this.el.nativeElement, this.config);
// @ts-ignore
this.el.nativeElement.initialize();
}
}
In your Component ts File add
schemas: [CUSTOM_ELEMENTS_SCHEMA]
Set your Swiper configuration.
Example:
import {Component, CUSTOM_ELEMENTS_SCHEMA, ViewEncapsulation} from '@angular/core';
import {CommonModule} from '@angular/common';
import {MainHeadingComponent} from "../main-heading/main-heading.component";
import {StreamItemComponent} from "./stream-item/stream-item.component";
import {A11y, Mousewheel, Navigation, Pagination, SwiperOptions} from 'swiper';
import {SwiperDirective} from "../../directives/swiper.directive";
@Component({
selector: 'fm-streams-swiper',
standalone: true,
encapsulation: ViewEncapsulation.None,
imports: [
CommonModule,
MainHeadingComponent,
StreamItemComponent,
SwiperDirective
],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
templateUrl: './streams-swiper.component.html',
})
export class StreamsSwiperComponent {
sliders: string[] = [
'Test 1',
'Test 2',
'Test 3',
'Test 4',
'Test 5',
'Test 6',
'Test 7',
'Test 8',
'Test 9',
]
public config: SwiperOptions = {
modules: [Navigation, Pagination, A11y, Mousewheel],
autoHeight: true,
spaceBetween: 20,
navigation: false,
pagination: {clickable: true, dynamicBullets: true},
slidesPerView: 1,
centeredSlides: true,
breakpoints: {
400: {
slidesPerView: "auto",
centeredSlides: false
},
}
}
}
And the HMTL File:
<swiper-container fmSwiper [config]="config" init="false" class="w-full">
<swiper-slide class="w-[310px] sm:w-[450px] pb-6"
*ngFor="let slider of sliders">
<fm-stream-item></fm-stream-item>
</swiper-slide>
</swiper-container>
This is my solution for the moment. Happy to hear better ways to implement the new Version of Swiper in Angular :-)
Swiper Element: Core Version & Modules
Upvotes: 31
Reputation: 19
I just upgraded a project from Swiper 8 to Swiper 9 and faced the same initial confusion. Apparently they did away with the Angular components in Swiper and want us to the Swiper Elements.
https://swiperjs.com/element#swiper-custom-elements-from-cdn
It is not an ideal "Angular" solution, but I expect they made this decision to be more compatible with as many frameworks as possible.
To make this work just create a div layout with the classes "swiper", "swiper-wrapper", and "swiper-slide" with the wrapper inside swiper and the slides inside the wrapper. In the ts file import Swiper and create a new instance of the class and direct it to file ".swiper".
My code looks like this:
this.swiper = new Swiper('.swiper', {
modules: [Navigation, Pagination, Autoplay],
speed: 4000,
effect: 'flip',
spaceBetween: 0,
navigation: false,
autoplay: {
delay: 3500,
disableOnInteraction: true
},
slidesPerView: 1,
pagination: { clickable: true },
scrollbar: { draggable: true }
});
Upvotes: 1