Cycs
Cycs

Reputation: 239

Filter a backbone collection based on multiple model attributes

I am wanting to filter a backbone collection so returned to me are are only models that match or are close too some search parameters,

My models structure looks like this,

{  
    email: "[email protected]",
    first_name: "Name",
    surname: "LastName",
    handle: "NameLastName",
    initials: "NL"  
}

The model above is "User" model, it can be in the "UsersCollection", what I am hoping is achievable, is to search the "UsersCollection" for models based on the email address, first name, last name and handle, at the moment, I can only search based on one attribute, below is the search function from the "UsersCollection",

search : function(letters){
    if(letters == "") return this;

    var pattern = new RegExp(letters,"gi");
    return _(this.filter(function(data) {
        return pattern.test(data.get("first_name"));
    }));
}

The letters parameter comes from the view, and they the value of a text input. Now this works, but it only creates matches based on the first_name, how could I match across multiple attributes?

Upvotes: 1

Views: 1748

Answers (3)

Andrew Crawford
Andrew Crawford

Reputation: 113

I know this is old. I solved the same thing today and thought I should post the code I'm using. I found a lot of this from another stackoverflow question. I can't find it now, sorry!

This will make a new array from a backbone collection. If you want another backbone collection you can do new BackboneCollection(search_results);.

var search_results = this.collection.filter(function(model) {
  return _.some(model.attributes, function(val, attr) {

    if(val !== null && val !==  undefined)
    {
      if(!_.isString(val))
      //if a model's attribute is a number, we make it a string.
      {
        val = val.toString();
      }
      //convert both options to uppercase for better searching ;)
      val = val.toUpperCase();
      q = q.toUpperCase(); //q is the search term, I grabbed it using JQuery

      var array = q.split(" ");
      return ~val.indexOf(q);
    }
    else
    {
      return false;
    }

  });
});

Note: This will not work for multiple words. If you were searching cars Volkswagen would work but not 1988 Volkswagen unless one of the model's attributes was "1988 Volkswagen" but they would more likely be two different attributes (year and make).

Upvotes: 0

fbynite
fbynite

Reputation: 2661

I do something similar as well. I map an input attribute to a model key and pass an object to search that has the attributes the user is searching for. The search function is sort of like this: Here is a minimal jsbin demo.

var Collection = Backbone.Collection.extend({
  model:yourModel,

  search:function(find) {
    return this.filter(function(model) {
      var json = model.toJSON();

      for (var k in find) {
        var re = new RegExp(find[k],"i");
        if (json[k].search(re) === -1) return false;
      }

      return true;
    });

   }
});

The html code looks like:

<input name="first_name"/>
<input name="last_name"/>
<input name="email"/>

The view code would be (more or less):

var View = Backbone.View.extend({
  template:_.template($('#view-template').html()),
  events: {
    'keyup input' : 'search'
  },

  search:function() {
    var find = {};

    this.$('input').each(function() {
      var $element = $(this),
          modelKey = $element.attr('name'),
          modelVal = $element.val();
      find[modelKey] = modelVal;
    });

    var results = this.collection.search(find);

    /* do something with results */

  }
});

This seems to work decently if you don't have a large data-set. You could also use indexOf and toLowerCase if you didn't want to use a RegExp object. Maybe this will get you started down some path.

Upvotes: 0

StateLess
StateLess

Reputation: 5402

now it will return the models that match either of the attribute.

  var UsersCollection = Backbone.Collection.extend({
      model: User,

      myFilter: function( email, first_name, surname , handle) {
        filtered = this.filter(function(user) {
          return user.get("email") === email || user.get("first_name") === first_name ||
                 user.get("surname") === surname || user.get("handle") == handle ;
          });
        return new UsersCollection(filtered);
      }

    });

    var filterdMe = UsersCollection.myFilter("[email protected]","something1","something2","something3");

Upvotes: 0

Related Questions