33

I am looking how to pass data secretly between two separate components (not parent and child) without using URL params in my Vue2 app. This doesn't mean I am passing secrets but rather I just dont want the user to see it (only for UI considerations).

I know Vue has Props but they are meant for passing data between parent and child component. In my case, my URL will change but I don't want to pass data via visible params.

Someone claimed to use props without URL params here but I haven't been able to reproduce a working solution (getting undefined each time).

I also checked out these options but they are all using either URL or query params which as we know are visible.

An ugly solution would be to write the data to local storage and then read it there but this creates a lot of overhead and complexity (like what if I only want this data to be read once, etc).

Is there a more elegant solution to this problem?

Thanks!

mayank1513
  • 8,767
  • 6
  • 37
  • 99
KasparTr
  • 2,098
  • 5
  • 23
  • 51
  • [vuex](https://vuex.vuejs.org) – Lawrence Cherone Jun 23 '18 at 05:45
  • vuex store will have the same complexity problems as local storage. Also vuex lifecycle depends on page refresh. If the router redirects to another view, doesn't that constitute as a refresh and reset anything set there in the previous view? – KasparTr Jun 23 '18 at 06:17
  • A javascript router should not refresh the page when changing route, just update url and state. I think the sensible thing is to make api calls and persist on your backend. It has become such a normal thing and it is less awkward than cookies or sessions (at least most of the time). – ippi Jun 23 '18 at 07:12
  • @ippi thanks. I will try vuex with its overhead. As far as making API calls to backend I am not sure what exactly you mean here but in this use-case, making a separate API call to server will not work, also it would be subscribing brain surgery to a headache :) – KasparTr Jun 23 '18 at 07:21
  • Alright! But if your problem is to persist data between page reloads, then that is not the problem vuex solves (you'll still need localstorage or some other storage to use with vuex). If your problem is to get your app to not refresh on route change, then that's either a webserver configuration problem or not setting html5 history mode correctly (or not using your url-hash correctly). – ippi Jun 23 '18 at 08:18

3 Answers3

28
  1. make props: true for the destination route -- in the index.js file of router

    {
      path: '/home',
      name: 'home',
      component: taskChooser,
      props: true,
      }
  1. define prop in the component e.g props: ['myprop'], - note the quotes

  2. copy the variable you want to pass from the source route into the same name as your prop - in this case myprop

myprop = theVariableThatYouWantToPass

this.$router.replace({name:'home', params:{myprop}});

Make sure that the name of prop and variable are same - the prop is in quotes.

It's working for me.

mayank1513
  • 8,767
  • 6
  • 37
  • 99
  • That is not working, as the props in the other end is still undefined. Currently I am accessing the props with `this.myprop` and it gives my undefined even though it is defined at the point of `router.replace`. Variable name and prop name equals. – KasparTr Jul 17 '18 at 02:41
  • 2
    See point 2 please. In the component where you want to receive props, you must define props. – mayank1513 Oct 24 '18 at 15:49
  • 1
    just add props: ['myprop'], line above your data(){} – mayank1513 Oct 24 '18 at 15:50
  • In order to make this work, I had to adjust your final line to be syntactically complete for JSON => `this.$router.replace({name:'home', params:{ homeComponentPropertyName: currentComponentVariableName }});` – Collierton Dec 13 '18 at 17:44
  • please accept the answer if it works - you might edit the line you are mentioning - so that it may help others with similar issue. – mayank1513 Jan 09 '20 at 11:35
  • 2
    It works with name routes, but not with path routes, fine line. – Ricky-U Jul 02 '20 at 16:43
18

Thanks @Mayank for pointing me in the correct direction.

Here is the correct syntax that worked for me.

  1. Notice the props in In router index

    {
      path: '/componentPath',
      name: 'componentName',
      props: {
         header: true,
         content: true
      },
    }
    
  2. In the component you are redirecting to, define the props as following:

    props: {
      myProperty: {
        type: <DATATYPE>
      },
    }
    
  3. Perform redirect as following:

    this.$router.push({
      name: 'componentName',
      params: {
        myProperty: <VARIABLE>
      }
    })
    
  4. Access props with the this. convention from created or in later lifecycle event.

In this case, the variable name and property name do not have to be the same as it is a simple map. That naming convention would be a curious design choice anyway.

KasparTr
  • 2,098
  • 5
  • 23
  • 51
  • 1
    This does not work: https://github.com/vuejs/vue-router/issues/444#issuecomment-204657658 – Andrei Savin Jul 17 '19 at 19:24
  • 1
    This works, if you navigate with a vue router to a `/componentPath`, but if you just paste a link in a browser to your path, then these props won't be there. – Alexander Kim Aug 27 '19 at 08:40
  • 4
    To make it work you have to take it from `this.$route.params` not from `props`. A prop is not necessary. – Rudy Feb 03 '20 at 18:50
  • Thanks @Rudy! `this.$route.params` worked perfectly for me! Didn't need to add a prop at all – Chris Nov 23 '20 at 18:11
1

I haven't tested this in Vue 2, but in Vue 3, you can pass a stringified object through the props when you click on a link:

  1. Add props: true to your routes file, for the route.

         {
             path: 'receipt',
             name: 'receipt',
             component: () => import('../pages/Receipt.vue'),
             props: true,
             beforeEnter(to, from, next) {
                 if (!to.params.receiptData) {
                     return next({
                         name: 'dashboard',
                         params: {
                             locale: from.params.locale ? from.params.locale : 'en',
                         },
                     });
                 }
    
                 return next();
             },
         },
    
  2. Include your stringified object as a param for router.push().

        const receiptData = {
             transferType: 'default',
             recipient: receiver.value.name,
             referenceNumber: '#B3423424234',
             amountSent: formAmount,
             transferFee: 0,
         };
    
         router.push({
             name: 'receipt',
             params: {
                 receiptData: JSON.stringify(receiptData),
             },
         });
    
  3. Declare the props as instance data in the component.

     <script setup>
     import { computed } from 'vue';
    
     const props = defineProps({
       receiptData: {
           type: String,
           required: true,
       },
     })
     console.log('receiptData', props.receiptData);
    
     const parsedReceiptData = computed(() => JSON.parse(props.receiptData));
    
     </script>
    

I haven't tested an upper limit for size, so be careful about passing a huge object through, and you'll notice I showed a beforeEnter middleware on the route too because, if the user presses F5 to refresh the page, the props will be lost, so in my case, I redirect the user away from the page because the receipt is for one time use only.

agm1984
  • 12,436
  • 6
  • 68
  • 94