Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Backbone Collection - Filtering and Rendering the Collection loses reference to the original unfiltered Collection

I'm setting up an app driven by Backbone. I'm facing a "should-be-simple" problem, where I have a Model called "Message", a Collection called "MessageList", and Views called "MessageView" and "MessageListView".

The MessageListView code renders the MessageList. I have 4 toggle buttons that filter what the MessageListView shows. The filter buttons are "All", "Active", "Flagged", and "Ignored". "All" is the initial filter on the page load. When a user presses the "Flagged" filter, only Messages with a flag==1 should appear. When "All" is pressed again, all the Messages should appear again.

The problem I'm running into, and the problem in my design, is that when I filter the Collection based on the filterString, the reference to the original entire Collection gets lost. So, when "All" is pressed again, Messages have been lost.

I'm curious as to the best way to do this in Backbone...

Here's the setup code...

    var messageListView = new MessageListView({collection: messageList});

Here's the MessageListView code...

MessageListView = Backbone.View.extend({

    initialize : function() {

        this.collection.on("add", function(model) {
            var view = new MessageView({model: model});
            $("div.cameras").prepend(view.render().el);
        });

        this.collection.on("remove", function(model) {
            var ID = model.id;
            $("#message-" + ID).parent("div.message").remove();
        });

        this.collection.on("reset", function(models) {
            $("div.cameras").empty();
            models.each(function(message) {
                var view = new MessageView({model: message});
                $("div.cameras").prepend(view.render().el);
            });
        });

    },

    filterMessages : function(filterString) {
        var filtered = this.collection.filter(function(model){

            if (filterString == "all")
            {
                return true;
            }
            else if (filterString == "active")
            {
                return model.get("ignore") == "0";
            }
            else if (filterString == "ignore")
            {
                return model.get("ignore") == "1";
            }
            else if (filterString == "flag")
            {
                return model.get("flag") == true;
            }

        });
        this.collection.reset(filtered);
    },
like image 952
bluedevil2k Avatar asked Apr 12 '12 16:04

bluedevil2k


1 Answers

When you call

this.collection.reset(filtered)

You're throwing away the old data and replacing it with the new data.

What you want to do instead is to have an interim 'collection' (array or Backbone.Collection) to hold the results of the filter and THAT is what you use as the 'data source' for rendering your Messages to the DOM.

There's a couple ways you can do this.

  1. Just render the [array] output of the filterMessages function in every case (including the 'all' case) - but don't feed that result back into the original collection.
  2. Make a second collection to receive the results of the filterMessages function, and render that one, again, leaving your original collection intact.
like image 102
Edward M Smith Avatar answered Nov 14 '22 22:11

Edward M Smith