0

my setting

  1. I have a component which shows a data table.

  2. The data can be filtered by entering stuff into fields which values are stored in the components props.

  3. I also have vue-router in place to route between this and other components.

my desires

  1. I want to share the state of my filtered table with some team member by providing an url for him or her.

  2. When the url is accessed, the props get allocated the values from the url and voila I get the same list.

  3. The parameters are not all needed everytome. So if I only filter by one of the fields, I will only need that param to pass in the url.

my questions

  1. How would I do this?
  2. And which pitfalls did't I see?

What I tried so far...

...was playing around with this.$router.push() function by watch()ing the props change; but I couldn't figure how to avoid the reloading of the page on the one hand and how to not adapt the server config on the other hand (as I want the route to work without any parameters as well).

I also dind't figure out so far how to define dynamic urls in the router config ... is there a way, or do you create aliases for every possible configuration, if you want to pass a different set of params every time?

helle
  • 11,183
  • 9
  • 56
  • 83
  • The page won't be reloaded - Vue will reuse the component instance and just patch it. You will notice that by the fact of `beforeRouteUpdate` hook firing instead of `beforeRouteEnter` hook. And for your usecase it is more appropriate to use URL query values instead of route path parameters. – IVO GELOV May 06 '21 at 10:21
  • I recently needed an optional route param, and found this: https://stackoverflow.com/questions/47824660/optional-param-in-vuejs-router) – Tim May 06 '21 at 13:55
  • thanks for your comments. @ivo can you provice more information about "URL query values" thanks – helle May 06 '21 at 15:36

1 Answers1

3

Instead of registering route like /path/:parameter_1/:parameter_2 you should actually register /path and then use this.$router.push('/path?parameter_1=xxx&parameter_2=yyy')

This is an excerpt from my code - the data-table component will emit pagination event when the page/sorting/rowsPerPage is changed and also on the initial rendering. Since we are changing only the URL query values (the text after ? in the URL) - Vue will reuse the component so we listen to beforeRouteUpdate to react on the new parameters in the URL and fetch the appropriate data:

  data () 
  {
    return {
      filters:
      {
        parameter_1: this.$route.query.parameter_1 || '',
        parameter_2: this.$route.query.parameter_2 || '',
      }
    }
  },
  watch:
  {
    filters:
    {
      deep: true,
      handler: 'filtrationHasChanged'
    }
  },
  beforeRouteUpdate (to, from, next) 
  {
    this.fetchData(to);
    next();
  },
  methods:
  {
      buildQuery (route) 
      {
        const query = route ? route.query : this.$route.query;
        let params = '?';
        if (query.parameter_1) params += '&parameter_1=' + query.parameter_1;
        if (query.parameter_2) params += '&parameter_2=' + query.parameter_2;
        return params;
      },
      fetchData (route) 
      {
        this.$ajax.get('/api_endpoint' + this.buildQuery(route)).then(response =>
        {
          // use the API response
        });
      },

      filtrationHasChanged (filters) 
      {
        const obj = Object.assign({}, this.$route.query);

        if (filters.parameter_1) 
        { 
          obj.parameter_1 = filters.parameter_1; 
        }
        else 
        { 
          delete obj.parameter_1; 
        }

        if (filters.parameter_2) 
        { 
          obj.parameter_2 = filters.parameter_2; 
        }
        else 
        { 
          delete obj.parameter_2; 
        }

        // check if old and new query are the same - VueRouter will not change the route if they are, so probably this is the first page loading
        if (this.isSameObject(query, obj)) 
        { 
          this.fetchData(); // page has been manually reloaded in the browser
        } 
        else 
        {
          this.$router.replace({
            ...this.$router.currentRoute,
            query: obj
          });
        }
      },

      isSameObject (a, b) 
      {
        let same = true;
        for (const key in a) 
        {
          if (a[key] != b[key]) 
          {
            same = false;
            break;
          }
        }
        // to handle the case when a KEY exists in B but not in A
        for (const key in b) 
        {
          if (a[key] != b[key]) 
          {
            same = false;
            break;
          }
        }
        return same;
      }
  }
IVO GELOV
  • 13,496
  • 1
  • 17
  • 26
  • Can you try to improve your answer, by excerpting those parts that really matter and comment out your specific parts, so that we create general knowledge for the SO community ? – helle May 17 '21 at 07:29
  • @helle Sure, here it is. – IVO GELOV May 17 '21 at 08:51
  • thanks. What is `filtrationHasChanged` for and where does it get called? – helle May 17 '21 at 13:30
  • @helle It is a simple watcher which navigates to the new route than the user updates the filters. – IVO GELOV May 17 '21 at 14:27
  • this wont work for back and forward buttons on the browser as the url will simply change in the top but the data wont – PirateApp Jun 19 '21 at 05:35
  • 1
    @PirateApp If BACK/FORWARD button leads to the same route but with different query parameters - `beforeRouteUpdate` hook will catch this. IF they are different routes - then we also need `mounted` or `beforeRouteEnter` hook. – IVO GELOV Jun 20 '21 at 08:17