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?
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);
}
}