1

I am new to Angular CLI and I am using v11. I am trying to create an image gallery that clicking on some image generates a modal with a carousel showing the selected image at the beginning. But when I click on the image and the modal appears, for some reason the @ViewChild decorator, it always returns undefined.

My template code is the following:

<mat-card>
  <mat-card-subtitle>Gallery</mat-card-subtitle>
  <mat-card-content>
    <ng-template id="mySlides" #myModal class="h-100 h-auto" let-d="dismiss">
      <div class="modal-header">
        <h4 class="modal-title">Gallery</h4>
        <button type="button" class="close mt-1" aria-label="Close" (click)="d('Cross click')">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-content">
        <ngb-carousel #myCarousel>
          <ng-template ngbSlide *ngFor="let img of images" id="{{img.id}}">
            <div class="picsum-img-wrapper">
              <img src="{{img.url}}" alt="Image {{img.id}}">
            </div>
          </ng-template>
        </ngb-carousel>
      </div>
    </ng-template>
    <div class="row text-center text-lg-left">
      <div class="col-lg-3 col-md-4 col-6" *ngFor="let img of images">
        <a style="cursor: pointer" class="d-block mb-4 h-100"
           (click)="openModal(myModal); setSlideId(img.id);navigateToSlide(img.id)">
          <img class="img-fluid img-thumbnail" src="{{img.url}}" alt="">
        </a>
      </div>
    </div>
  </mat-card-content>
</mat-card>

My ts code is the following:

import {Component, ViewChild} from '@angular/core';
import {NgbCarousel, NgbCarouselConfig, NgbModal} from '@ng-bootstrap/ng-bootstrap';


@Component({
  selector: 'kt-image-gallery-thumbnail',
  templateUrl: './image-gallery-thumbnail.component.html',
  styleUrls: ['./image-gallery-thumbnail.component.scss'],
  providers: [NgbCarouselConfig]

})

export class ImageGalleryThumbnailComponent {

  selectedSlide = 1;

  constructor(private modalService: NgbModal) {
  }

  images = [{
    id: 1,
    url: '../../../../assets/media/products/product1.jpg'
  }, {
    id: 2,
    url: '../../../../assets/media/products/product12.jpg'
  }, {
    id: 3,
    url: '../../../../assets/media/products/product11.jpg'
  }];

  @ViewChild('myCarousel', {static: false, read: NgbCarousel}) myCarousel: NgbCarousel;

  openModal(content: any) {
    this.modalService.open(content);
  }

  navigateToSlide(item) {
    console.log(this.myCarousel);
    try {
      this.myCarousel.select('' + item);
    } catch (e) {
      console.log(e);
    }

  }

  setSlideId(id: number) {
    console.log(id);
    this.selectedSlide = id;
  }
}

Thanks for the help.

2 Answers2

1

remove #myCarousel from template

Then in your typescript file simple use

@ViewChild(NgbCarousel) 
myCarousel: NgbCarousel;

Edit: Reading the whole template I see major structure problems, or I have missed something.

       <div class="row text-center text-lg-left">
            <div class="col-lg-3 col-md-4 col-6" *ngFor="let img of 
           images">
           <a style="cursor: pointer" class="d-block mb-4 h-100"
          (click)="openModal(myModal); 
          setSlideId(img.id);navigateToSlide(img.id)">
        <img class="img-fluid img-thumbnail" src="{{img.url}}" alt="">
        </a>
         </div>
    </div>

1)How do you expect to get the img.id on setSlide(img.id) and also in navigateToSlide(img.id)? You are already outside of ngFor. This information is gone.

2)I see that in one component you have all the modal declared and also the code to invoke it. Consider moving all the modal content into a separate component and leave here only the content that it represents as a page without the modal. Then everything will be more clear for you to inspect why something does not work.

3)I think with all references # you have tried on a clever way to bypass a more complex code that should be implemented. As you can see ngbCarousel belongs only inside the modal so it is right that it works only inside of modal and not outside of it. If you clear your structure maybe you can understand exactly what breaks and why.

Panagiotis Bougioukos
  • 15,955
  • 2
  • 30
  • 47
  • Thanks for your answer but it didn't work either. The only way that partially works is adding this method to the ts ``setCarousel(carousel: any) {this.myCarousel = carousel;}`` and in the template in the modal something like this ``Image {{img.id}}`` then when the modal appears and I make click on some image, ``this.myCarousel`` returns the elementRef but no when I try from outside the modal. – Roger Godofredo Rivero Morales Jan 27 '21 at 20:42
  • @Panagiotis, I have faced the similar issue and removing #CarouselId from template and simply using `@ViewChild(NgbCarousel) myCarousel:NgbCarousel; ` this resolved my issue. But I am not able to understand why removing #carouselId from template made it work? what is the underlying reason? and what to do in case I have multiple carousel item , how to identify them? Can you please share some knowledge ? – shantanu ghosh Mar 22 '23 at 07:44
0

I finally got it to work from Panagiotis Bougioukos' recommendation and I think it's simpler than what I had done. The solution is the next:

  1. I created a new component with the carousel and called it from the modal.

Component Gallery-Carousel

HTML Template

<ngb-carousel #myCarousel [activeId]="item">
  <ng-template ngbSlide *ngFor="let img of images" id="{{img.id}}">
    <div class="picsum-img-wrapper">
      <img src="{{img.url}}" alt="Image {{img.id}}">
    </div>
  </ng-template>
</ngb-carousel>

TS Code

import {Component, Input, ViewChild} from '@angular/core';
import {NgbCarousel} from '@ng-bootstrap/ng-bootstrap';


@Component({
  selector: 'kt-gallery-carousel',
  templateUrl: './gallery-carousel.component.html',
  styleUrls: ['./gallery.carousel.component.scss'],
})

export class GalleryCarouselComponent {

  @Input() item: string;

  @ViewChild('myCarousel', {static: true, read: NgbCarousel}) myCarousel: NgbCarousel;

  constructor() {
  }

  images = [{
    id: 1,
    url: '../../../../assets/media/products/product1.jpg'
  }, {
    id: 2,
    url: '../../../../assets/media/products/product12.jpg'
  }, {
    id: 3,
    url: '../../../../assets/media/products/product11.jpg'
  }];
}
  1. I modified my Image Gallery component

Component Image Gallery with Modal

HTML Template

<mat-card>
  <mat-card-subtitle>Gallery</mat-card-subtitle>
  <mat-card-content>
    <ng-template id="mySlides" #myModal class="h-100 h-auto" let-d="dismiss">
      <div class="modal-header">
        <h4 class="modal-title">Gallery</h4>
        <button type="button" class="close mt-1" aria-label="Close" (click)="d('Cross click')">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-content">
        <kt-gallery-carousel [item]="selectedSlide"></kt-gallery-carousel>
      </div>
    </ng-template>
    <div class="row text-center text-lg-left">
      <div class="col-lg-3 col-md-4 col-6" *ngFor="let img of images">
        <a style="cursor: pointer" class="d-block mb-4 h-100"
           (click)="openModal(myModal); setSlideId(img.id)">
          <img class="img-fluid img-thumbnail" src="{{img.url}}" alt="">
        </a>
      </div>
    </div>
  </mat-card-content>
</mat-card>

TS Code

import {Component} from '@angular/core';
import {NgbModal} from '@ng-bootstrap/ng-bootstrap';


@Component({
  selector: 'kt-image-gallery-thumbnail',
  templateUrl: './image-gallery-thumbnail.component.html',
})

export class ImageGalleryThumbnailComponent {
  selectedSlide: string;

  constructor(private modalService: NgbModal) {
  }

  images = [{
    id: 1,
    url: '../../../../assets/media/products/product1.jpg'
  }, {
    id: 2,
    url: '../../../../assets/media/products/product12.jpg'
  }, {
    id: 3,
    url: '../../../../assets/media/products/product11.jpg'
  }];

  openModal(content: any) {
    this.modalService.open(content);
  }

  setSlideId(id: number) {
    this.selectedSlide = '' + id;
  }
}