1

I've built a Vue JS search component to search and filter a list of properties for a property website. The search component is listed on every page, so it makes sense for me to use a URL search query which takes me to the main properties page and then use something like this.$route.query.search to get the value from my query and store in a variable.

The property data is coming from a JSON file which essentially looks like this:

{
  "data": [
    {
      "id": 12,
      "sold": false,
      "isFeatured": true,
      "slug": "green-park-talbot-green-cf72-8rb",
      "address": "Green Park, Talbot Green CF72 8RB",
      "county": "Rhondda Cynon Taf",
      "price": "695000",
      "features": ["Modern fitted kitchen", "Integrated appliances", "Front and rear gardens"],
      "type": "Semi-Detached",
      "bedrooms": "3"
    }
}

My search query would be something like this:

/properties/?search=Address%20Here&type=Apartment&bedrooms=2&county=Maesteg

Which then would filter each thing.

How this works is quite simple, inside my data object I have my variables which get each query and store them as follows:

data () {
    return {
      searchAddress: this.$route.query.search,
      searchType: this.$route.query.type,
      searchBedrooms: this.$route.query.bedrooms,
      searchCounty: this.$route.query.county
    }
}

And then I have a filter inside the computed area called filteredProperties which filters down the properties inside the v-for which isn't necessary to show here:

computed: {
    filteredProperties: function(){
      return this.properties.filter((property) => {
        return property.address.match(this.searchAddress) && property.type.match(this.searchType) && property.bedrooms.match(this.searchBedrooms) && property.county.match(this.searchCounty)
      });
    }
  }

Now this works absolutely fine and works correctly... however I now need to modify this to instead of having <select> dropdowns which is how you would currently pick the number of bedrooms, or the property type etc, I now need to replace the property type <select> dropdown with checkboxes so that the user can select multiple property types and essentially add that as an array into the URL.

I'm not quite sure how to modify this part of my filter to be able to look for multiple property types:

property.type.match(this.searchType)

Many thanks

UPDATE

I've recently tried updating my computed filter with the following:

computed: {
    filteredProperties: function(){
      return this.properties.filter((property) => {

        return property.address.match(this.searchAddress) &&
        this.searchAddress.some(function(val){
          return property.search.match(val)
        }) &&

        property.type.match(this.searchType) &&
        this.searchType.some(function(val){
          return property.type.match(val)
        }) &&

        property.bedrooms.match(this.searchBedrooms) &&
        this.searchBedrooms.some(function(val){
          return property.bedrooms.match(val)
        }) &&

        property.county.match(this.searchCounty) &&
        this.searchCounty.some(function(val){
          return property.county.match(val)
        })

      });
    }
  }

I need the search to work with and without a URL query.

Also tried an if/else statement:

computed: {
    filteredProperties: function(){
      return this.properties.filter((property) => {
        return property.address.match(this.searchAddress) &&

        if (this.searchType.length > 1) {
          this.searchType.some(function(val){
            return property.type.match(val)
          })
        } else {
          property.type.match(this.searchType)
        } &&

        property.bedrooms.match(this.searchBedrooms) &&
        property.county.match(this.searchCounty)
      });
    }
  }

UPDATE

I got it working by doing the following:

computed: {
    filteredProperties: function(){
      return this.properties.filter((property) => {

        let searchTypeMatch;

        if (typeof this.searchType === "object") {
          searchTypeMatch = this.searchType.some(function(val){
            return property.type.match(val)
          })
        } else {
          searchTypeMatch = property.type.match(this.searchType)
        }

      return property.address.match(this.searchAddress) &&
        searchTypeMatch &&
        property.bedrooms.match(this.searchBedrooms) &&
        property.county.match(this.searchCounty)
      });
    }
  }
2
  • hi just curious why you are using match(...) to see if the given address is contained in your data set ? Also, after you change it to checkbox are you guaranteed that you will get an array in your query parameter. If yes then the modification will be pretty lean using .some(...) of the Array.prototype. Commented Sep 25, 2018 at 18:11
  • Could I change .match() to .some() for that particular one then? And what other options are there? Commented Sep 25, 2018 at 18:21

2 Answers 2

2

You will have to use JSON for the query parameters in order to serialize/deserialize the arrays.

data () {
    return {
      searchAddress: this.$route.query.search ? JSON.parse(this.$route.query.search) : [],
      searchType: this.$route.query.type ? JSON.parse(this.$route.query.type) : [],
      searchBedrooms: this.$route.query.bedrooms ? JSON.parse(this.$route.query.bedrooms) : [],
      searchCounty: this.$route.query.county ? JSON.parse(this.$route.query.county) : []
    }
}
computed: {
    filteredProperties: function()
    {
      return this.properties.filter((property) => 
      {
        return (this.searchAddress.length ? this.searchAddress.some((address) =>
        {
          return property.address.match(address);
        }) : true) && (this.searchType.length ? this.searchType.some((type) =>
        {
          return property.type.match(type);
        }) : true) && (this.searchBedrooms.length ? this.searchBedrooms.some((bedrooms) =>
        {
          return property.bedrooms.match(bedrooms);
        }) : true) && (this.searchCounty.length ? this.searchCounty.some((county) =>
        {
          return property.county.match(county);
        }) : true)
      });
    }
  }

Then you send query params like this

this.$router.push("/search?search=" + JSON.stringify(searchArray) 
  + "&type=" + JSON.stringify(typeArray)
  + "&bedrooms=" + JSON.stringify(bedroomsArray)
  + "&county=" + JSON.stringify(countyArray)
);
Sign up to request clarification or add additional context in comments.

5 Comments

I just tried this, I get: Unexpected token S in JSON at position 0 when trying to search
Probably the argument for JSON.parse() has not been generated by JSON.stringify() - you have to first serialize your array(s) before being able to deserialize them.
this.$router.push("/search?search="+JSON.stringify(searchArray)+"&type="+JSON.stringify(typeArray)+"&bedrooms="+JSON.stringify(bedroomsArray)+"&county="+JSON.stringify(countyArray));
This sounds cool, but it's a generic <form> element with a button with an action that points to /properties which is my page, not /search. Furthermore, it looks like I'd have to tie that code into a click event? So what if they simply press enter? I'm using a get request.
whether it is /search or /properties is irrelevant. You should bind to the submit event on the form itself (it will catch the Enter) - then prepare the proper URL, set the form.action to this URL and then return true
1

I have not worked with routing in Vue apps but for the following to work you will have to ensure that this.$route.query.search (and the other route properties) is an (are) [](s).

return this.searchAddress.some(function(val) {
    return property.address.match(val)
}) && 
this.searchType.some(function(val){
    return property.type.match(val)
}) && ...

Let me know if this works for you.

RE-EDITED:

Hi, please change the computed property to the following

computed: {
    filteredProperties: function () {
        let self = this
        let routeConstraints = ['search', 'type', 'bedrooms', 'county'].filter(function (val) {
            return self.$route.query[val] !== undefined
        })
        if (routeConstraints.length === 0) {
            return self.properties
        } else {
            return routeConstraints.reduce(function (acc, val) {
                return acc.filter(function (property) {
                    //basically I am checking if there is some value in the query parameter that matches properties.
                    if (self.$route.query[val] !== undefined) {
                        //check if the route parameter is an array (object)
                        if (typeof this.searchType === "object") {
                            self.$route.query[val] = [self.$route.query[val]]
                        }
                    }
                    return self.$route.query[val].some(function (item) {
                        //changed the matching condition to indexOf
                        return property[val].match(item).length > 0
                    })
                })
            }, self.properties)
        }
    }
}

Basically,

  1. I am trying to check what routes values are set from your checkbox selections
  2. Using it to filter the properties array.
  3. If none are set then all properties are returned.

Hope this works.

15 Comments

I don't think this will work, I'll try the .some()
This uses .some(..). May I ask why this won't work ?
If I change property.type.match(this.searchType) to property.type.some(this.searchType), it doesn't work. I also tried using your code, that doesn't work either :/
hi, thanks for checking. could you please print Array. isArray( this.searchType) and check if that is true and a typeof this.searchType. Thanks.
Okay, an update... I've got it to work now, it returns true and took a bit on tinkering, however, If there's only one item in the array (e.g: one property type) and not multiples it gives me an error: _this.searchType.some is not a function but works fine with more than one?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.