ChristopherLeeMiller

Archives concerning all matters web development and beyond

From my Desk

Multi Filtering, or why I went crazy for a month trying to do it

Published: 05/20/2019 | Time To Read:4mins

So let me start with a pre-face on why this came up and how I worked it through till I finally came up with the rather hacky solution that works!

One of the goals with my new website was to build a dedicated models page. A space where I can show off the work I've put into building models over the last few years, but also make it easy enough for visitors to my site to be able to navigate and sort and search for what they want to see rather easily. Basic things that all kits have in common are the grand, the scale, if they are completed or not (some are still in progress), and tags that i've added. As time has gone on many new ones have been built and I needed a way for users to narrow down their search results as well, say you want a specific brand in a specific scale that isn't done.

My first pass through was to just add dropdown boxes with all the options possible (populated from GraphQL queries) and just push the new URL with that one variable changed. It worked but you could only select one thing, not more, that presents a problem with trying to sort down to specific subset.

From there I began researching the best method to handle this mult-sort. I knew I wanted to build a function that could be re-used wherever routing was in place on the page so that everything could take advantage of it, but the implementation details I wasn't sure on.

First Attempt

The first version of the function accepted the current router query params, the slug for the item to update, and the new value and the idea was to take the query params which were already an object and just update the slug with the value. But this lead to some very strange behaviors with things that were never even clicked on being selected and a lot of null values being appended. Hours of debugging lead me nowhere.

I'm still thinking even though I was saving a copy of the query params that I was somehow mutating the actual state of the router in the end which caused the behavior but i'm not 100% certain.

Reverting back

At that point I decided it was better to just revert functionaility back to how I had it before rather than leaving a broken system in place, even if it meant a user couldn't multi-select options. Thankfully this was rather clean and easy to do having used the function already.

Here is that function:

export function urlBuilder(slug, value) {
  return `/models?${slug}=${value}`;
}

Simple enough right, take the slug and value and slap them onto the path and call it a day.

Final Solution

After much thought and time passing I decided it was time to revisit this problem and get it working once and for all. The idea this time was to pass in the current URL as a string with the path and all being there. Then take and split out the path from the params. From there I take params string and split it again into each individual param component that then gets split into a key value pair. This key, value is then stored into an object. After that a basic check is done to see if the new value is empty or not.

An empty new value has special meaning to my query system as it represents an 'All' sort, which by all merits means I don't want to filter by it any longer. If its not empty we simply update the object from the previous step with the new value based on the provided slug and if it is we delete it from the object entirely.

The last step is to then serialize the object and append it to the path, returning back the combined string and we get the correct result. Are there better ways of handling this? I'm sure, but this seems to work and I noticed no slowdowns in change of a dropdown.

Here is the final version of the overall function with the serialize function also displayed:

function serialize(obj) {
  return `?${Object.keys(obj)
    .reduce((a, k) => {
      a.push(`${k}=${encodeURIComponent(obj[k])}`);
      return a;
    }, [])
    .join(`&`)}`;
}

export function urlBuilder(current, slug, value) {
  const [path, paramString] = current.split(`?`);
  const params = paramString.split(`&`);
  const paramsObject = [];

  params.forEach(param => {
    const [key, value] = param.split(`=`);
    paramsObject[key] = value;
  });

  if (value != ``) {
    paramsObject[slug] = value;
  } else {
    delete paramsObject[slug];
  }

  return `${path}${serialize(paramsObject)}`;
}

With that piece of code in place multi-selectng of the filtering works!

Caveats

One problem that still remains and i've gotta figure out is how to use this and it work with pagination, currently the pagination works but only without filters. I need to find a way to make them work together.

Contact Me

Reach out today with any questions or concerns.

Follow Me

Be sure to see the latest and greatest

GithubLinkedInInstagramTwitterYouTube

Use of this site constitues acceptance of our Privacy Policy. The material on this site may not be reproduced, distributed, transmitted, cached or otherwise used, except with prior written permission of Christopher Lee Miller.

Copyright © 2022v1.24.8