bootstrap-vue: Cannot access $bvToast in Nuxt plugin

Describe the bug

I want to display API errors as toast messages. I have to use a Nuxt plugin to work with Axios error interceptors. The plugin has access to the Vue instance app from the Nuxt context. However, the app instance is missing the $bvToast injection. So calls like app.$bvToast.toast('some toast message') fail.

Steps to reproduce the bug

  1. Create a Nuxt plugin called /plugins/axios-toast.js
export default function ({ $axios, app }) {
  $axios.onError(error => {
    app.$bvToast.toast(error)
  })
}
  1. Reference the plugin in nuxt.config.js
  plugins: [
    '~plugins/axios-toast.js'
  ],
  1. Intentionally trigger an invalid route
<b-button @click="$axios.$get('badroute')">Do it</b-button>
  1. Observe the error in the browser

TypeError: Cannot read property ‘toast’ of undefined

Expected behavior

The Vue instance app in the Nuxt plugin context should have the $bvToast injected.

Versions

Libraries:

  • BootstrapVue: 2.4.1
  • Bootstrap: 4.4.1
  • Nuxt 2.11.0
  • Nuxt Axios 5.9.5

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 1
  • Comments: 16 (9 by maintainers)

Most upvoted comments

I used this: $nuxt.$bvToast.toast(error) in my Axios interceptor and works fine!

In case someone is using Client-Side Rendering only, this issue can be solved by using window.$nuxt

For e.g.

export default function ({ $axios }) {
  $axios.onError(error => {
    window.$nuxt.$bvToast.toast(error)
  })
}

it works:

export default function({ store, app: { $axios } }) {
  $axios.onError(error => {
    store._vm.$bvToast.toast('There is an error', {
      variant: 'danger',
      autoHideDelay: 5000,
    })
  })
}

There actually was a limited debugging offered for errors in plugins and my code had an error in it. Due to this it didn’t work when I tried. It looks like you can do this via $nuxt available in the plugins:

export default function ({app, $axios}) {
  $axios.onError(err => {
    if (err.response.status === 400) {
      $nuxt.$bvToast.toast(
        err.response.data.message,
        {
          title: app.i18n.t('error'),
          variant: 'danger',
          autoHideDelay: 5000
        }
      );
    }
  });
}

The app key on the nuxt context object is not the $root Vue instance, but rather it is the options object used to define the app: https://nuxtjs.org/api/context#universal-keys

If you were to check context.app._isVue you will find that _isVue is undefined as contet.app is not an actual Vue instance, and that there are no properties like $root, or $parent, etc, defined on it.

$bvToast requires a Vue instance to work.

In your plugin, you could try:

import Vue from 'vue'

export default function ({ $axios }) {
  $axios.onError(error => {
    const instance = new Vue({})
    instance .$bvToast.toast(error)
  })
}

But note that this will be a singleton Vue instance that doesn’t share the same $root as your app, so events will not be emitted onto the app $root

$nuxt.$bvToast.toast

How are you accessing $nuxt? This is in context with ~/plugin/axios.js

I wouldn’t instantiate a new copy of the instantiated app, as that would double your memory footprint and potentially have adverse affects with your app.