Angular Slider - Build Angular Carousel From Scratch Tutorial
Angular Slider - Build Angular Carousel From Scratch Tutorial

In this post we will implement a slider inside Angular without usage of any additional libraries.

Finished project

Architecture

So how do we plan to implement our slider inside Angular? The idea is to render a slider component and provide slides from the outside.

<image-slider [slides]="slides"></image-slider>

Also it is important the our slider is fluid and takes the width and height of it's parent.

<div style="width: 500px; height: 200px; margin: 0 auto">
  <image-slider [slides]="slides"></image-slider>
</div>

Now let's prepare a list of slides in our parent component.

export class AppComponent {
  slides = [
    { url: '/assets/image-1.jpeg', title: 'beach' },
    { url: '/assets/image-2.jpeg', title: 'boat' },
    { url: '/assets/image-3.jpeg', title: 'forest' },
    { url: '/assets/image-4.jpeg', title: 'city' },
    { url: '/assets/image-5.jpeg', title: 'italy' },
  ];
}

This is a list of objects with url and title. Why it is not just array of strings? Later you might need some additional information. And it is much easier to add this information if you have here objects and not just strings.

Now the question is from where we are getting this images? I already prepared these images for our slider and you can take them from the source code at the end of the post.

Creating module

Now we are ready to start implementing our module. We don't want to create just a component, we want to fully isolate our feature inside an additional module.

// src/app/imageSlider/imageSlider.module.ts
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { ImageSliderComponent } from './components/imageSlider/imageSlider.component';

@NgModule({
  imports: [CommonModule],
  exports: [ImageSliderComponent],
  declarations: [ImageSliderComponent],
})
export class ImageSliderModule {}

Here we defined our module and registered our future ImageSliderComponent.

Now I want to create an interface for each slide. It will make our code more understandable and easier to support.

// src/app/imageSlider/types/slide.interface.ts
export interface SlideInterface {
  url: string;
  title: string;
}

Here we defined properties like we write them in app.component. Now we can specify our slides type.

export class AppComponent {
  slides: SlideInterface[] = [
      ...
    ];
  }

Now let's add imageSlider component to our module. And actually I already prepared CSS for our slider so we can focus only on our logic.

// src/app/imageSlider/components/imageSlider/imageSlider.component.css
.slide {
  width: 100%;
  height: 100%;
  border-radius: 10px;
  background-size: cover;
  background-position: center;
}

.rightArrow {
  position: absolute;
  top: 50%;
  transform: translate(0, -50%);
  right: 32px;
  font-size: 45px;
  color: #fff;
  z-index: 1;
  cursor: pointer;
}

.leftArrow {
  position: absolute;
  top: 50%;
  transform: translate(0, -50%);
  left: 32px;
  font-size: 45px;
  color: #fff;
  z-index: 1;
  cursor: pointer;
}

.slider {
  position: relative;
  height: 100%;
}

.dotsContainer {
  display: flex;
  justify-content: center;
}

.dot {
  margin: 0 3px;
  cursor: pointer;
  font-size: 20px;
}

Now let's define our basic component.

// src/app/imageSlider/components/imageSlider/imageSlider.component.ts
@Component({
  selector: 'image-slider',
  templateUrl: './imageSlider.component.html',
  styleUrls: ['./imageSlider.component.css'],
})
export class ImageSliderComponent {
  @Input() slides: SlideInterface[] = [];
}

We defined our component and our slides array as an input.

Adding logic

Now we successfully registered our module and we can start writing logic of our slider. For our slider we just need a single property and it is currentIndex. This property will help us to know what slide is active now. And all our slides inside array are going with indexes.

export class ImageSliderComponent {
  @Input() slides: SlideInterface[] = [];

  currentIndex: number = 0;
}

Now let's create markup for our slider.

<div class="slider">
  <div>
    <div class="leftArrow"></div>
    <div class="rightArrow"></div>
  </div>
  <div
    class="slide"
    [ngStyle]="{ 'background-image': getCurrentSlideUrl() }"
  ></div>
</div>

Instead of providing an inline background image I wrote here a function getCurrentSlideUrl which we must create now.

export class ImageSliderComponent {
  ...
  getCurrentSlideUrl() {
    return `url('${this.slides[this.currentIndex].url}')`;
  }
}

This function just returns a url of current slide.

Basic slider

As you can see we successfully rendered our basic slider.

Adding arrows

Now we need to add events to implement our arrows. Let's add them to our markup.

<div>
  <div (click)="goToPrevious()" class="leftArrow"></div>
  <div (click)="goToNext()" class="rightArrow"></div>
</div>

Now we need to implement them in our component.

goToPrevious(): void {
  const isFirstSlide = this.currentIndex === 0;
  const newIndex = isFirstSlide
    ? this.slides.length - 1
    : this.currentIndex - 1;

  this.currentIndex = newIndex;
}

goToNext(): void {
  const isLastSlide = this.currentIndex === this.slides.length - 1;
  const newIndex = isLastSlide ? 0 : this.currentIndex + 1;

  this.currentIndex = newIndex;
}

So goToNext must increment our currentIndex and goToPrevious decrement it. But the problem is that we want to jump to the first slide if we are clicking next on the last slide and jump to last slide when we click previous on the first slide. This is exactly what we are going here.

As you can see in browser we can jump with arrows through our slides.

Jumping to direct slide

But it is not all. Typically you have a possibility to jump to a specific slide. So you have something like dots on the bottom where you can click to jump.

Let's add markup for our dots.

<div class="slider">
  <div>
    <div (click)="goToPrevious()" class="leftArrow"></div>
    <div (click)="goToNext()" class="rightArrow"></div>
  </div>
  <div
    class="slide"
    [ngStyle]="{ 'background-image': getCurrentSlideUrl() }"
  ></div>
  <div class="dotsContainer">
    <div
      *ngFor="let slide of slides; let slideIndex = index"
      class="dot"
      (click)="goToSlide(slideIndex)"
    ></div>
  </div>
</div>

So here we looped through our slides and rendered a dot for each of them. Also we created click event with goToSlide function that we must implement.

goToSlide(slideIndex: number): void {
  this.currentIndex = slideIndex;
}

Here we simply jump to the specific index.

Finished project

As you can see our slider is successfully implemented.

And actually if you want to learn Angular with NgRx from empty folder to a fully functional production application make sure to check my Angular and NgRx - Building Real Project From Scratch course.

📚 Source code of what we've done