Search code examples
javascriptvue.jsbootstrap-vue

Bootstrap Vue b-toast will not trigger a second time


I have an array of event messages which I want to display one at a time as pop-up messages which don't interfere too much with other things going on in my web app.

I've found a nice component with Bootstrap Vue b-toast.

The toast displays the 1st array element then, when the toast "hidden" event is detected, the array is "shifted" to remove the first event message. It should then redisplay the toast using the new first array element.

However the second call to show the toast is being ignored.

I've been working with a simple case:

<div>
  <button v-on:click="displayEvents">Display events</button>
  <b-toast id="event-toast" static no-close-button auto-hide-delay="2000">
    {{ events[0] }}
  </b-toast>
</div>

{
  data() {
    return {
      events: []
    }
  },
  methods: {
    displayEvents() {
      this.events.push("Event 1");
      this.events.push("Event 2");
      if (this.events.length >= 1) {
        console.log('Show 1st toast - events now: ' + this.events);
        this.$bvToast.show('event-toast');
        // this.$root.$emit('bv::show::toast','event-toast')
      }
      this.$root.$on('bv::toast:hidden', bvEvent => {
        if (bvEvent.componentId == 'event-toast') {
          this.events.shift();  // Remove first element as it has now expired
          console.log('removed first element - events now: ' + this.events);
          if (this.events.length > 0) {
            console.log('Display next event');
            this.$nextTick(() => { 
              this.$bvToast.show('event-toast');
            })
            // this.$root.$emit('bv::show::toast','event-toast') 
          }
        }
      });
    }
  }
}

The console logging indicates that it is hitting the correct code to trigger the toast, which uses the same method to trigger it second time which is working first time.

I've also tried the "emit" method of triggering the toasts (currently commented out).

Any ideas what the problem might be? Have I made an error in the coding, or have I found a bug in Bootstrap Vue?

Thanks in advance!


Solution

  • There are two issues. displayEvents is registering another event listener each time it runs. This only needs to be done once so move the listener registration to, for example, created:

    created() {
       this.$root.$on('bv::toast:hidden', bvEvent => {
       // ...
       });
    }
    

    I believe there's a race condition when the 2nd event is emitted, in which the DOM change is still being propagated (i.e. hidden, in this case) from the first event.

    Try this:

    if (this.events.length > 0) {
       console.log('Display next event:');
       this.$nextTick(() => {
          this.$bvToast.show('event-toast');
       })
    }
    

    nextTick defers the callback until after the next DOM update cycle. The Vue API doc gives this tip:

    Use it immediately after you’ve changed some data to wait for the DOM update.