How to wrap ngb-carousel?

47 Views Asked by At

I have an instance of ngb-carousel which is currently used like this:

<ngb-carousel>
    <ng-template ngbSlide>Hello</ng-template>
    <ng-template ngbSlide>World</ng-template>
</ngb-carousel>

I would like to wrap it into my custom carousel component providing some extra features, to use it like this:

<!-- my-carousel.component.html -->
<div class="my-carousel">
    <ngb-carousel>
        <ng-content></ng-content>
    </ngb-carousel>
    <div>Some text here</div>
</my-carousel>

<!-- usage -->
<my-carousel>
    <ng-template ngbSlide>Hello</ng-template> 
    <ng-template ngbSlide>World</ng-template>
</my-carousel>

However, this naive approach does not work, as the slides are not being displayed.

I have tried to use <ng-content select="ng-template[ngbSlide]" ngProjectAs="ng-template[ngbSlide]"> and other combinations to no avail.

What am I missing? Which is the proper way to do it?

1

There are 1 best solutions below

1
Naren Murali On BEST ANSWER
  • We need to create templates with a template reference slide.

  • Then we can take a ContentChildren to get all the slides through content projection

  • after getting the slides, the key is to use a ng-template with the attribute ngbSlide for the ngb-carousel to recognize it as a slide

  • We can use @for to loop through the slides and render them inside a ng-template using ngTemplateOutlet

Working example below

parent html

@if (images) {
<my-carousel>
  <ng-template #slide>
    <div class="picsum-img-wrapper">
      <img [src]="images[0]" alt="Random first slide" />
    </div>
    <div class="carousel-caption">
      <h3>First slide label</h3>
      <p>Nulla vitae elit libero, a pharetra augue mollis interdum.</p>
    </div>
  </ng-template>
  <ng-template #slide>
    <div class="picsum-img-wrapper">
      <img [src]="images[1]" alt="Random second slide" />
    </div>
    <div class="carousel-caption">
      <h3>Second slide label</h3>
      <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
    </div>
  </ng-template>
  <ng-template #slide>
    <div class="picsum-img-wrapper">
      <img [src]="images[2]" alt="Random third slide" />
    </div>
    <div class="carousel-caption">
      <h3>Third slide label</h3>
      <p>Praesent commodo cursus magna, vel scelerisque nisl consectetur.</p>
    </div>
  </ng-template>
</my-carousel>
}

parent ts

import { Component } from '@angular/core';
import { NgbCarouselModule } from '@ng-bootstrap/ng-bootstrap';
import { MyCarouselComponent } from './my-carousel/my-carousel.component';

@Component({
  selector: 'ngbd-carousel-basic',
  standalone: true,
  imports: [NgbCarouselModule, MyCarouselComponent],
  templateUrl: './carousel-basic.html',
})
export class NgbdCarouselBasic {
  images = [944, 1011, 984].map((n) => `https://picsum.photos/id/${n}/900/500`);
}

my carousel ts

import { CommonModule } from '@angular/common';
import {
  Component,
  ContentChildren,
  OnInit,
  QueryList,
  TemplateRef,
} from '@angular/core';
import { NgbCarouselModule } from '@ng-bootstrap/ng-bootstrap';

@Component({
  selector: 'my-carousel',
  template: `
  <div class="my-carousel">
    <ngb-carousel>
      @for(slide of slides; track slide; let i = $index) {
      <ng-template ngbSlide >
        <ng-template
         [ngTemplateOutlet]="slide"></ng-template>
      </ng-template>
      }
    </ngb-carousel>
    <div>Some text here</div>
  </div>
  `,
  standalone: true,
  imports: [NgbCarouselModule, CommonModule],
})
export class MyCarouselComponent implements OnInit {
  @ContentChildren('slide', { read: TemplateRef }) slides: QueryList<
    TemplateRef<any>
  >;
  constructor() {}

  ngOnInit() {}

  ngAfterContentInit() {
    console.log(this.slides);
  }
}

stackblitz