4

Is it possible to redirect a user to another page after successfully fetching/retrieving data? In this case, it's about verifying a user's email address. When the user clicks on given link in the email, he is redirected to a page where his token is sent to the backend and verified.

If the response is successful, I would like to automatically redirect the user to the "/home" page. Unfortunately it doesn't work this way.

If-statement is working, because I receive the response in the console.

<script>
export default {
  async fetch() {
    this.res = await this.$axios.$post(
      `/api/verify-mail/${this.$route.params.id}`
    )

    if (this.res.success) {
      this.$router.push('/home')
      console.log(this.res)
    }
  }
}
</script>

Edit: (Improved code)

If-condition is definitely true. I receive the response in the console. { success: true, message: 'Your account has been successfully verified' }

So, condition is working and it's writing in the console, but the router doesn't push.

If it's outside of the condition, it doesn't work too. But I discovered something interesting. If I force an error, the router can push me to the '/home' page.

async fetch() {
   try {
     const res = await this.$axios.$post(
       `/api/verify-mail/${this.$route.params.id}`
     )

     if (res.success) {
       console.log(res)
       this.$router.push('/home')
     }
   } catch (err) {
     this.$router.push('/home') // only for testing, if I force an error I will be redirected
   }
}

home.vue

<template>
  <div>
    <div v-if="getUser.isVerified === false">
      <IsVerified />
    </div>
    <div>
      <h1>Home</h1>
    </div>
  </div>
</template>

<script>
import { mapGetters } from 'vuex'
import IsVerified from '../components/verify/IsVerified.vue'
export default {
  auth: false,
  components: { IsVerified },
  computed: {
    ...mapGetters(['getUser']),
  },
}
</script>

verify-mail/_id.vue

<template>
  <div>Test</div>
</template>

<script>
export default {
  data() {
    return {}
  },
  async fetch() {
    try {
      const res = await this.$axios.$post(
        `/api/verify-mail/${this.$route.params.id}`
      )

      if (res.success) {
        console.log(JSON.parse(JSON.stringify(res)))
        console.log(res)
        this.$router.push('/home')
      }
    } catch (err) {
      this.$router.push('/home') // only for testing, if I force an error
    }
  },
}
</script>

<style></style>

IsVerified.vue Component (/components/verify/IsVerified.vue)

<template>
  <div class="container">
    <div class="message">
      <p>
        Please confirm your email address
        <span class="bold">{{ getUser.email }}</span> to get full access. Token
        is only avaible for 30 minutes.
      </p>
      <button v-show="isShow" @click="sendNewToken()">Send again</button>
      <p v-show="mailSent" class="mail__sent">Email sent</p>
    </div>
  </div>
</template>

<script>
import { mapGetters } from 'vuex'
export default {
  data() {
    return {
      isShow: true,
      mailSent: false,
    }
  },

  computed: {
    ...mapGetters(['getUser']),
  },

  methods: {
    async sendNewToken() {
      try {
        const send = await this.$axios.$put('/api/send-new-verify-token', {
          data: this.getUser,
        })
        if (send.success) {
          this.isShow = false
          this.mailSent = true
        }
      } catch (err) {
        console.log(err)
      }
    },
  },
}
</script>

package.json

{
  "name": "frontend",
  "version": "1.0.0",
  "private": true,
  "scripts": {
    "dev": "nuxt",
    "build": "nuxt build",
    "start": "nuxt start",
    "generate": "nuxt generate",
    "lint:js": "eslint --ext \".js,.vue\" --ignore-path .gitignore .",
    "lint": "npm run lint:js"
  },
  "dependencies": {
    "@nuxtjs/auth-next": "5.0.0-1624817847.21691f1",
    "@nuxtjs/axios": "^5.13.6",
    "@nuxtjs/recaptcha": "^1.0.4",
    "core-js": "^3.15.1",
    "nuxt": "^2.15.7",
    "sass": "^1.37.5"
  },
  "devDependencies": {
    "@babel/eslint-parser": "^7.14.7",
    "@nuxtjs/eslint-config": "^6.0.1",
    "@nuxtjs/eslint-module": "^3.0.2",
    "babel-eslint": "^10.1.0",
    "eslint": "^7.32.0",
    "eslint-config-prettier": "^8.3.0",
    "eslint-loader": "^4.0.2",
    "eslint-plugin-nuxt": "^2.0.0",
    "eslint-plugin-prettier": "^3.4.0",
    "eslint-plugin-vue": "^7.15.1",
    "prettier": "^2.3.2",
    "sass-loader": "^7.3.1"
  }
}

nuxt.config.js

const BackendURL = 'http://localhost:4000'

export default {
  head: {
    title: 'frontend',
    htmlAttrs: {
      lang: 'en',
    },
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      { hid: 'description', name: 'description', content: '' },
      { name: 'format-detection', content: 'telephone=no' },
    ],
    link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }],
  },

  css: [
    '~/assets/css/main.scss',
    '~/assets/fonts/Nunito.css',
    '~/assets/css/style.css',
  ],

  components: true,

  buildModules: ['@nuxtjs/eslint-module'],

  modules: ['@nuxtjs/axios', '@nuxtjs/auth-next', '@nuxtjs/recaptcha'],

  axios: {
    proxy: true,
    baseURL: BackendURL,
  },

  proxy: {
    '/api': BackendURL,
  },

  auth: {
    strategies: {
      local: {
        token: {
          property: 'token',
          type: 'Bearer',
          name: 'token',
        },
        user: {
          property: false,
        },
        endpoints: {
          login: { url: '/api/login', method: 'post' },
          user: { url: '/api/user', method: 'get' },
          logout: { url: '/api/logout', method: 'post' },
        },
      },
    },

    redirect: {
      login: '/', // User will be redirected to this path if login is required
      logout: '/', //User will be redirected to this path if after logout, current route is protected
      callback: '/home', //User will be redirected to this path by the identity provider after login
      home: '/home', // User will be redirected to this path after login. (rewriteRedirects: true, ... will rewrite this path)
    },
    rewriteRedirects: false,
  },

  recaptcha: {
    hideBadge: true, // Hide badge element (v3 & v2 via size=invisible)
    language: 'english',
    size: 'normal', // Size: 'compact', 'normal', 'invisible' (v2)
    siteKey: 'secret',
    version: 2,
  },

  build: {
    babel: {
      plugins: [
        ['@babel/plugin-proposal-private-property-in-object', { loose: true }],
      ],
    },
  },
}
PhilippeUn
  • 190
  • 2
  • 16
  • This should work properly. Are your sure `this.res.success` is truthy? Double-check this one. Maybe try to remove the condition and see if it works properly. Also, try using a local `res = await this...` variable rather than a `this.res` because of lifecycles. You can always set `this.res = res` below if the redirect is not successful. – kissu Aug 10 '21 at 12:01
  • Hello kissu, thanks for your help! I know you, you helped me a few months ago :-) I've edited the question. – PhilippeUn Aug 10 '21 at 12:45
  • Btw, where is this code called? `fetch()` is available pretty much anywhere but still, just to know the flow of your app. – kissu Aug 10 '21 at 12:54
  • Also try `console.log(JSON.parse(JSON.stringify(res)))` and check if it returns what you expect properly. – kissu Aug 10 '21 at 12:57
  • 1
    The code is called in an usual page, no component. `/pages/verify-mail/_id.vue` With your console statement, the response is also returned as expected. – PhilippeUn Aug 10 '21 at 13:05
  • Alright, so you do have `nuxt/auth`, I guess that you're using the middleware at some point. Can you put a `auth: false` in your `home.vue` page? If it's protected. – kissu Aug 10 '21 at 13:14
  • Makes no different :( – PhilippeUn Aug 10 '21 at 13:17
  • Please edit your question with the whole code of your `home.vue` and `verify-mail/_id.vue` page please. – kissu Aug 10 '21 at 13:18
  • Alternatively, I will build a button for the user to redirect himself. Not the best user experience, but it should work bug-free :-) – PhilippeUn Aug 10 '21 at 14:05
  • You should not have to import components manually if you have set `components: true` in your `nuxt.config.js` file. You can use this setting to not have to specify the paths: https://stackoverflow.com/a/66336654/8816585 Otherwise, looking at the given code, I'm not sure what is the issue here. It should work fine. – kissu Aug 10 '21 at 16:39
  • I also don't understand it. But anyway, thanks very much for your time and help! I learned a few new things! :-)) – PhilippeUn Aug 10 '21 at 18:13
  • If you have a public github repo, we could maybe look into it. But not sure that we can do better as of right now. – kissu Aug 11 '21 at 08:54
  • just change fetch -> mounted :) – joseph NK Mar 18 '22 at 02:56
  • @josephNK usually if you have some tools, rather use them. – kissu Mar 18 '22 at 04:31

3 Answers3

2

With the new version of fetch (nuxt >= 2.12), you should use: this.$nuxt.context.redirect({path:'/404'});

OOM
  • 728
  • 7
  • 22
naccio
  • 51
  • 4
1

Your way should work. Try to log this.$router to see if it's available / contains route information.

If you don't need res later you don't have to await the promise. Redirect to /home when the request was successfull, otherwise catch the error and to nothing.

<script>
export default {
  fetch() {
    this.$axios
      .$post(` / api / verify - mail /${this.$route.params.id}`)
      .then(() => {
        this.$router.push("/home");
      })
      .catch(error => {
        //handly error that happened during the call
      });
  }
};
</script>

This way it works without using a res object.

nonNumericalFloat
  • 1,348
  • 2
  • 15
  • 32
  • Why would one not await the response in an `async` fetch() hook? It's the main purpose of it. Using `.then` is doing basically the same, it's just an older syntax. No plue value. – kissu Aug 10 '21 at 12:22
  • `This way it works without using a res object`, so if the payload received a `200` response with a `this.res.success` set to `false`, you're redirecting here? Not every API is well thought. – kissu Aug 10 '21 at 12:24
  • Does it need to be async? Maybe it is just very convenient as `$fetch` can be called as a method & is accessed on route navigation or on server side. – nonNumericalFloat Aug 10 '21 at 12:24
  • Whatever API you are using but if it returns a `200` it should be a correct response. – nonNumericalFloat Aug 10 '21 at 12:26
  • It's a server call, in a JS context, it totally need to be async. Especially for the lifecycle of the page/component. `fetch()` works as fine on server and yeah, you can use `$fetch` but I don't see the point here. – kissu Aug 10 '21 at 12:26
  • Yep, so a `200` response is fine for you? It doesn't say if OP's use-case is truthy at all tho. Since it can be `200` but OP wants to check a specific field on it. It's kinda bypassing the main thing by justifying the same result. – kissu Aug 10 '21 at 12:27
  • Still, `async/await` is totally fine and the issue is probably not coming from this flow. – kissu Aug 10 '21 at 12:28
  • ok chef, I accept your willingness to help. – nonNumericalFloat Aug 10 '21 at 12:30
0

Try this kind of code and see if it works in your use case

<script>
export default {
  async fetch() {
    const postedObject = await this.$axios.$post('https://jsonplaceholder.typicode.com/posts', {
      title: 'foo',
      body: 'bar',
      userId: 1,
    })
    console.log('posted object >>', postedObject)
    if (postedObject.title === 'foo') {
      // try with 'baz', it will not router-push
      this.$router.push({ name: 'home' })
    }
  },
}
</script>

This kind of code should totally work. If not, there is maybe a middleware or some specific issue with your app's configuration.
A brand new Nuxt app may be required for testing purposes.

kissu
  • 40,416
  • 14
  • 65
  • 133
  • Console statement returns as expected. Router doesn't push... – PhilippeUn Aug 10 '21 at 13:06
  • You have an issue with your Nuxt app so. Can you please share your `package.json` and `nuxt.config.js` file? Also, can you show us the whole content of your page component? @Antz – kissu Aug 10 '21 at 13:07
  • Done. btw, how do I insert the code the correct way here? So that it is colorized. – PhilippeUn Aug 10 '21 at 13:13
  • @antz check my edits, usually it's `3backticks` and either `js`, `html` or `json`. Click on `edit` and look how I edit after you. – kissu Aug 10 '21 at 13:15