0

I enter my site through a reverse proxy server that is passing a token to my site. I want to be able to force that token onto the query string every time I go to a different route and I want to be able to do that without putting the query string on every "to" in any button I may have. This way if my users reload the page, they don't lose their token.

I am attempting to do this in my default.vue page so that no matter what page I am on it pushes the query string into the route. The problem I am facing is this works great when the user single clicks a button taking them to the route, however when the button is double clicked the querystring isnt being pushed onto the route.

Here is my default.vue and a snip it of the button that does the page change.

....default.vue.....

  <template>
    <v-app>
    <banner :text="My Banner Text"/>
    <v-container fluid px-0 py-0>
     <v-main>
      <nuxt />
     <v-main>
    </v-container>
    </v-app>
    </template>
    
    <script>
      import banner from '@/components/layouts/default/banner'
      export default {
       components: {
         banner
        }, 
    data() => {
      return {
        landingRoute: null,
      }
     }, 
    async fetch(){
     if(this.isEmpty(this.$route.query)){
      const landingkey = this.landingRoute
      this.$router.push({name: this.$router.name, query: {info: landingKey}})
    },
    watch:{
      '$route.query': '$fetch'
    },
    mounted(){
    this.landingRoute = this.$route.query.info
    }, 
    methods: {
         isEmpty(json){
            return Object.keys(json).length === 0
         }
    }
 }
    </script>

...... mainNav.vue .......

This is the portion that is relevant

<v-btn
   to="/request"
><span>Request</span>
</v-btn>
Jamie
  • 428
  • 6
  • 24
  • Not sure about the whole context and if it is actually relevant but your issue is mainly "my route is not updated when button is double clicked while it is working when clicked once", right? If you check Vuetify's documentation (I'm supposing you're using this one): https://vuetifyjs.com/en/api/v-btn/#props you can see that passing the `to` prop is actually transforming it into a router link, hence why a double click may not work. Did you inspect your Vue devtools to actually see all the emitted events there? Also, did you tried with a regular method while using `@click`? – kissu Nov 10 '21 at 18:09
  • The route changes, as my page changes, however the route when double clicked does not get the querystring pushed onto it in my async fetch method. If this is an issue with Vuetify what kind of workaround can I implement? – Jamie Nov 10 '21 at 18:24
  • As far as the events in my devtools I see a click, 3 route changed events and then a seperate click. Inside my route changed events, my payload is an array, one that has the "/request" is the fullPath and one that has "/?info=xyz" as the fullPath – Jamie Nov 10 '21 at 18:35
  • Also here, you will probably need to add [a debounce](https://stackoverflow.com/a/67209352/8816585) to not spam your actual events but have only one event emitted. I don't even see the use case for a double-click to be honest. Is it more of a bug or more of a feature? – kissu Nov 10 '21 at 18:39
  • It's a bug, when I single click the query param gets added, when I double click it does not. From what I can tell that has to do with how you mentioned vuetify behaves on the to= Also I am not familiar with debounce, what is the purpose of debounce? – Jamie Nov 10 '21 at 19:13
  • Check this article to understand how debounce works: https://redd.one/blog/debounce-vs-throttle Then, you will be able to implement it thanks to my previous comment, on top of using a `@click` rather than the `to` prop on our button. Then, it should work properly even if you spam it 30 times in .4 seconds! – kissu Nov 10 '21 at 22:11

2 Answers2

1

i think you can handle this using router middleware as it is explained in nuxt document in this case in your nuxt.config.js file you will add middleware property to your router config:

router: {
    // other configs
    middleware: ['landingKey']
}

then you need to create middleware directory in your project root. in this directory create landingKey.js file which contains:

export default function ({ route, redirect, store }) {
    // saving key to store
    if (!store.state.landingKey) {
        store.commit('saveLandingKey', route.query.info)
    }
    // adding info to query if it doesnt exist
    if (!route.query || (route.query && !route.query.info)) {
        return redirect({name: route.name, query: {...route.query, info: store.state.landingKey}})
    }
}

with this solution you wont need to pass landingKey every time and also it will redirect if a route doesnt have landingKey. this all happens before mounting components. also if you need to use this middleware in specific pages and not globally, you can simply use middleware: ['landingKey'] in your page file.

also you can use cookie-universal-nuxt if you dont want to use vuex. so in this case, you have to do something like this:

export default function ({ route, redirect, app }) {
    if (!app.$cookies.get('landing-key')) {
        app.$cookies.set('landing-key', route.query.info, {
            path: '/',
            maxAge: 60 * 60 * 24 * 7
        })
    }
    // adding info to query if it doesnt exist
    if (!route.query || (route.query && !route.query.info)) {
        return redirect({name: route.name, query: {...route.query, info: app.$cookies.get('landing-key')}})
    }
}
kissu
  • 40,416
  • 14
  • 65
  • 133
mahdikmg
  • 833
  • 2
  • 6
  • 13
  • YES!! This is exactly what I was looking for. I'm still getting used to the differences between Vue and Nuxt, I knew I should have used middlewear I just was not aware of the redirect. Thanks. – Jamie Nov 11 '21 at 18:21
  • your welcome @Jamie, im glad i could help. yes nuxt keeps surprising me every time i get deeper in it. it has so many practical features. – mahdikmg Nov 11 '21 at 22:54
  • One final question. In my default.vue page I have put a async created() method that does a dispatch to my store to go get some data from one of our api's and then I use one specific part of that data to send it down to other "pages" that need it for display or calling other API's. I was doing this in the default.vue page as I only wanted to make that call one time, rather than doing it in several different pages. However the change I have implemented here has broken my $nuxt.$emit methods, as they only get called the first time I hit the site and not on any of the redirects. – Jamie Nov 12 '21 at 14:14
  • Moving my async created from default.vue to index.vue fixed the issue with the emit – Jamie Nov 12 '21 at 15:33
  • i know you fixed this and im late, but there are other ways to handle this and i want to tell you to know about them. as i understood, you want to get some static data from server that is common between all pages. so one way could be using [nuxtServerInit](https://nuxtjs.org/docs/directory-structure/store/#the-nuxtserverinit-action) in your vuex actions and call api there. the other way is calling api from your layout file and passing it through props on ``. i think you tried latter. also if you are calling that api only on index.vue page, then what will other pages do? – mahdikmg Nov 12 '21 at 18:10
  • if you only call this on index.vue page, then if user entered on other pages directly, your data won't fetched. right? – mahdikmg Nov 12 '21 at 18:19
  • I don't want my users hitting other pages directly (typing in the URL). This is a way of doing route guarding. – Jamie Nov 15 '21 at 13:39
0

The code snippet that you actually shared is pretty messy with a lot of errors. After some ESlint + Prettier, this is the result. Give it a try, it could work with just that.

/layouts/default.vue

<template>
  <v-app>
    <banner text="My Banner Text" />
    <v-container class="fluid px-0 py-0">
      <v-main>
        <Nuxt />
      </v-main>
    </v-container>
  </v-app>
</template>

<script>
import banner from '@/components/layouts/default/banner'

export default {
  components: {
    banner,
  },
  data() {
    return {
      landingRoute: null,
    }
  },
  fetch() {
    if (this.isEmpty(this.$route.query)) {
      const landingKey = this.landingRoute
      this.$router.push({
        name: this.$router.name,
        query: { info: landingKey },
      })
    }
  },
  watch: {
    '$route.query': '$fetch',
  },
  mounted() {
    this.landingRoute = this.$route.query.info
  },
  methods: {
    isEmpty(json) {
      return Object.keys(json).length === 0
    },
  },
}
</script>

Then, as I've told you in my comment, try to use the Vue devtools to inspect the events and the state at the same time. To narrow down your issue.

I find the code pretty strange by itself but the issue may probably come from the fact that you do have a mounted that is used to set landingRoute but an actual layout is not re-mounted AFAIR.

So yeah, please do the following steps and I'll do my best to help you if you still have some issues!

kissu
  • 40,416
  • 14
  • 65
  • 133
  • Sorry about that I was trying to write it up from my work PC which is not network connected, this is an internal non public facing app so. It's not a issue with the code itself on my PC. I did move the the this.$route.query.info into the fetch replacking thie this.landingRoute and got rid of mounted all together, that didn't seem to help. – Jamie Nov 10 '21 at 19:22