Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In-component Navigation Guard callback not working: Nuxt JS and `beforeRouteEnter`

I have a search form and a results page build with Nuxt JS. I am attempting to redirect the results page pages/results/index.vue back to the search page pages/search/index.vue if the form returns errors.

I am attempting to use In-Component Guards per the Vue documentation

According to the docs:

However, you can access the instance by passing a callback to next. The callback will be called when the navigation is confirmed, and the component instance will be passed to the callback as the argument:

beforeRouteEnter (to, from, next) {
  next(vm => {
    // access to component instance via `vm`
  })
}
// version info

├─┬ [email protected]
│ ├─┬ @nuxt/[email protected]
│ │ └─┬ @nuxt/[email protected]
│ │   └── [email protected]  deduped
│ └─┬ @nuxt/[email protected]
│   └─┬ @nuxt/[email protected]
│     └── [email protected]  deduped
└─┬ [email protected]
  └── [email protected]

My main issue is that the callback in the next() function in the navigation guard does not seem to work to re-route the page.

(the from page)

// page/search/index.vue

<template>
  ...
  <nuxt-link to="/results" @click.native="doSearch">
    Show Results
  </nuxt-link>
  ...
</template>

<script>
export default {
  ...
  methods: {
    doSearch () {
      ... // validates search fields and adds content to store
    }
  },
  ...
}
</script>

The above works fine, where doSearch validates the form and adds the results (along with any errors) to the store.

But then in the following...

(the to page)

// pages/results/index.vue

<script>
export default {
  ...
  beforeRouteEnter (to, from, next) {
    next((vm) => {
      console.log(vm.validateRoute()) // works: '/search'
      vm.validateRoute()              // does not work: does nothing
    })
  },
  ...
  computed: {
    errors () {
      return this.$store.state.errors
    }
  },
  ...
  async fetch ({ store, params }) {
    await store.dispatch('searchresults/GET_RESULTS')
  },
  ...
  methods: {
    validateRoute () {
      let route = true
      if (this.errors.length > 0) {
        route = '/search'
      }
      console.log(this.erros.length)  // works: 7
      console.log(route)              // works: '/search'
      return route
    }
  },
  ...
}
</script>

The callback in beforeRouteEnter does not appear to be evaluated and does not cause the route to change. Note the logging shows the callback is firing and returning the proper value(s).

If I explicitly define the route, without using a callback function, it works:

// pages/results/index.vue

<script>
export default {
  ...
  beforeRouteEnter (to, from, next) {
    next('/search')                   // works: re-routes to '/search' every time
  },
  ...
}
</script>

I tried several iterations of the next(callback) with limited success...

next(() => { return false })          // does not work
next(function () { return false })    // does not work

But only explicit declarations work...

next({ path: false })                 // works: prevents route change
next({ path: '/search' })             // works: changes route to '/search'

I'm at a total loss; is this a bug, or am I missing something?

Addendum

I previously tried using middleware as mentioned in the Nuxt documentation here. However this resulted in an endless loop, as discussed in this blog post.

// middleware/validate.js

export default function ({ store, redirect }) {
  console.log('middleware: validate')           // 'middleware: validate'
  if (store.state.errors.length > 0) {
    return redirect('/search')                  // ...endless loop
  }
  return true                                   // otherwise this works
}

// nuxt.config.js

export default {
  ...
  router: {
    middleware: "validate"
  },
  ...
}

Fixed

As pointed out by @ifaruki, placing the middleware call inside the page component fixes the endless loop issue:

Next step is to add your middleware to your page pages/results/index.vue like this:

export default {
   middleware: 'validate'
} 

I found this at the very end of the docs which appears to be the Nuxt method for Vue JS In-component Guards:

You can add your middleware to a specific layout or page as well:

pages/index.vue or layouts/default.vue

:facepalm:

like image 950
MonkishTypist Avatar asked Nov 07 '22 12:11

MonkishTypist


1 Answers

For this case nuxt.js has a property called middleware.

You could use the middleware instead of the guards. In your middleware folder you create first a file called for example validate.js.

In your validate.js file you create an function and put your validate logic in there:

export default function ({ store, redirect }) {
  if (store.state.errors.length > 0) {
    return redirect('/search')
  }
}

Now you have set up your middleware. Next step is to add your middleware to your page pages/results/index.vue like this:

export default {
   middleware: 'validate'
} 

The middleware is always called before the vue instance is created. Whenever store.state.errors is bigger 0, you will get redirected back to /search otherwise this middleware just gets ignored.

Always read the nuxt docs first because nuxt does not always have the same behaviour like vue.js or vue.js router Official docs: https://nuxtjs.org/api/pages-middleware

like image 199
Ifaruki Avatar answered Nov 12 '22 15:11

Ifaruki