4

I use angular 6 and I have a ngbCarousel that I want to add photos with a click of a button.

I use ngFor to create the HTML of the carousel.

<ngb-carousel #carousel *ngIf="images">
  <ng-template ngbSlide *ngFor="let im of images; let i = index">
    <img [src]='im.link' >
    <div>
      <input type="text" value={{im.name}} >    
      <input type="text" value='description of img' >    
      <input type="text" value='copyright owners' >          
      <button type="button" (click)='deletepic(i)'>delete</button>   
    </div>
  </ng-template>
</ngb-carousel>

<button type="button" (click)='addpic()'>add</button> 
<button type="button" (click)='last()'>last</button> 

The images array that contains JSON is this images= [{'link':'https://picsum.photos/900/500/?image=5','name':'first'},{'link':'https://picsum.photos/900/500/?image=574','name':'second'},{'link':'https://picsum.photos/900/500/?image=547','name':'third'}]; and when the addpic is clicked, another json is added in the array.

 addpic(){
    this.images.push({'link':'https://picsum.photos/900/500/?image=7','name':'added one'});     
      this.last()        ;   
  }

Also it has to force carousel to show the last, newly added photo. According to the API I have to use the select to navigate to a slide by its id, and the id is of the pattern ngb-slide-0.

So, last contains

  last(){
    let slide = 'ngb-slide-'+String(this.images.length-1);    
    this.carousel.select(slide); 
  }

The last photo is normally added to the carousel

The problem is that the last will not work when called from the addpic. It never goes to the last photo, even if the photo is added. If I put the

let slide = 'ngb-slide-'+String(this.images.length-1);    
this.carousel.select(slide); 

of last in the addpic will still not work. It always goes to the second to last photo (if I have 3 photos it always goes to the 3rd, after adding a 4th)

But this will work if I hit the last button or if I use the arrows in the carousel UI.

This makes me think that somehow the HTML or the array are not "refreshed" when the last is called in the typescript, but somehow the buttons refresh/create the HTML needed to show the right photo?

I dont know what it is, but I am almost certain now it has to do with html/typescript being refreshed. I think this is an angular issue and not a ngbCarousel issue. I dont know how to debug or proceed.

Please advice. Thanks

slevin
  • 4,166
  • 20
  • 69
  • 129

2 Answers2

2

After adding picture in array in addpic() method they take bit time to reflect in html as template so call last method in timeout like

addpic(){
    this.images.push({'link':'https://picsum.photos/900/500/?image=7','name':'added one'});     
    setTimeout(() => {
        this.last();
    });
  }
Paresh Gami
  • 4,777
  • 5
  • 23
  • 41
  • So, it boils down to a JS event loop issue. `setTimeout` is a dirty hack and I love as with all dirty hacks :) The problem is that the first time is called I can see a flash of blank `div` with no image inside. – slevin Sep 10 '18 at 11:13
1

You can use setTimeout to force the last method being executed after the changes has been detected by Angular, as in Paresh Gamis's answer.

But I think you could also use ChangeDetectorRef.detectChanges() in this situation, it feels less "hacky". (Documentation, and related Q&A here : What's the difference between markForCheck() and detectChanges() )

use it just before your last call.

I am not sure if it will work in your case (if you provide a StackBlitz minimal example, we could work on the details)

Pac0
  • 21,465
  • 8
  • 65
  • 74
  • `setTimeout` is great but gives a flash of empty carousel sometimes. About `detectChanges` : sounds awesome but I am an angular newbie and I dont have a clue what to do. What is the logic? Detect if the HTML `ngb-carousel` is changed and then call the `last` ? Can you please elaborate more and I will set up a stockBling or whatever you kids call it these days. Just kidding, I am setting a StackBlitz – slevin Sep 10 '18 at 11:23
  • I am sorry, I dont have a clue with this is blank, I imported the ng-bootstrap, the `bootstrap.css` and the necessary imports in the component. "It works in my machine" . I try to figure out whats is wrong [stackBlitz](https://stackblitz.com/edit/angular-9wcwcl?file=src%2Fapp%2Fapp.component.ts) – slevin Sep 10 '18 at 11:50
  • 1
    @selvin there are two errors which i can fix (and now I can see your example). First, there is an import line you need to delete in the component (MapcmsService), and you need to add ReactiveFormsModule in the imports of app.module.ts – Pac0 Sep 10 '18 at 12:05
  • Now I'm stuck, I can't add photos (it seems that stackblitz doesn't like picsum.photos ? Or is it my network corp that mess with HTTPS currently ?) – Pac0 Sep 10 '18 at 12:08
  • 2
    basically, I just wanted to try `this.changeDetectorRef.detectChanges(); this.last();` instead of `setTimeout(() => { this.last(); });` – Pac0 Sep 10 '18 at 12:11
  • I have some bad news.When you delete a photo and then add a new one, then I guess the `ngb-slide-XX` ids are getting messed up and I end up in the second-to-last photo again!!!! This is getting out of hand. Is there a way to just show the last photo? What frustrates me is that I have a simple array and I cannot force angular to just go to its end and somehow render what is inside. I cannot think of anything right now, if you have any advice,that would be great. Otherwise I dont know. I wll open an issue on `ng-bootstrap` I guess. I dont have time to build my own gallery... – slevin Sep 10 '18 at 12:26
  • 2
    Sorry for freaking out, there is a solution. I can have angular traverse the `ng-template ngbSlide` tag, get the last one, get its id and use this on the `this.carousel.select(actually_last_slide);` Working on it – slevin Sep 10 '18 at 12:52