Search code examples
javascriptbackbone.js

Navigating Views Backbone does not remove model


The problem is reproduced in the following way:

  1. Navigate to routeInitial State
  2. Navigate away Go to other route
  3. Navigate back Go back

Whenever I navigate back the models aren't rendered. I suppose that is because this events aren't triggered:

this.listenTo(this.collection, 'add', this.addOne);
this.listenTo(this.collection, 'reset', this.addAll);

This is my router:

    routes: {
        ''               : 'home',
        'home'           : 'home',
        'departments'    : 'departments',
        ...
    },

    home: function(){
        var view = new app.HomeView();
        this.showView(view);
    },

    departments: function(){
        var view = new app.DepartmentsView();
        this.showView(view);
    }, 
    showView: function(view){
         if (this.currentView){
          this.currentView.clean();
        }

        this.currentView = view;
        this.currentView.render()

        $('#container').html(this.currentView.el);
    }

And this is my clean method:

Backbone.View.prototype.clean = function () {
    this.remove();
    this.unbind();
    _.each(this.subViews, function(subView){
        subView.clean();
        if(subView.onClose){ subView.onClose() }
    });
};

This is the onClose method on subviews:

onClose: function(){
        this.model.off('change',this.render);
        this.model.off('destroy',this.remove);
        console.log('unbinding');
    }

I save all subviews on array and then close when navigating away. I really haven't identified the root cause of such problem.

I'm quite desperate because I have tried everything from Derick Bailey's posts and going through Backbone's docs and haven't been able to fix this.

------EDIT-------- The view is composed as:

  1. Parent View: Containing a the table, header, without the rows.
  2. Child View: Which are the rows --> <tr>
  3. Modal View: This is a view containing the form that creates the models

This is my repo if you want to take a look. Reproducing something minimal actually is a lot of code to post in a question. REPO

Ill appreciate help with this.


Solution

  • Here's what i suspect the problem is. Your Departments collection is only instantiated once, app.Departments = new DepartmentList();. Then in your DepartmentsView init function, you assign this.collection = app.Departments; This means that the second time you route to DepartmentsView, you're assigning its collection to an existing collection, that already has models. When you call fetch(), Backbone detects there are no new models (since you already have the models in the collection), and thus doesn't trigger the add events.

    One thing you can do is call fetch with reset, fetch({reset:true}). Then when the fetch succeeds, it'll clear the existing models and re-add them all, and trigger reset.

    Reset is a bit wasteful since you already have the models. Another solution is to check if the collection contains any models in your init function, if it does then render them. Something like this

    initialize: function(){
            this.collection = app.Departments;
            this.subViews = [];
    
            if(this.collection.size() > 0)
                this.collection.each(this.addOne, this);
    
            this.listenTo(this.collection, 'add', this.addOne);
            this.listenTo(this.collection, 'reset', this.addAll);
    
            this.collection.fetch();
        },
    

    Then the fetch will add any new models it finds upon fetch success.

    EDIT: Not the cleanest solution but it should work.

    initialize: function(){
            this.collection = app.Departments;
            this.subViews = [];
    
            if(this.collection.size() > 0)
                this.preExisting = true;
    
            this.listenTo(this.collection, 'add', this.addOne);
            this.listenTo(this.collection, 'reset', this.addAll);
    
            this.collection.fetch();
        },
    
        render: function(){
            this.$el.html( this.template({title: 'Departamentos',header_fields: this.tableHeader}) );
            this.$tbody = this.$('#rows');
    
            if(this.preExisting)
                this.collection.each(this.addOne, this);
    
            return this;
        },