How to correctly remove whatever is necessary from backbone view?

拥有回忆 提交于 2020-01-14 06:28:10

问题


Hey my attempt to remove everything was calling the close function in the search view file after triggering the previous or next functions with this.close() but it was removing the view completely affecting the navigate function... I was wondering how to go about removing everything after it is completed, otherwise the vent.on occurs multiple times in the router.

Here is my router file:

define([
    'jquery',
    'underscore',
    'backbone',
    'views/page',
    'models/search',
    'views/search',
    'text!templates/search.html',
    'models/song',
    'text!templates/song.html'


], function($, _, Backbone, PageV, SearchM, SearchV, SearchT, SongM, SongT) { 
    var vent = _.extend({}, Backbone.Events);
    var currentView, page, search;
    Backbone.View.prototype.close = function () {
        this.remove();
        this.unbind();
        if (this.onClose) { 
            this.onClose();
        }
    };

    var AppRouter = Backbone.Router.extend ({
        routes: {
            'page/:id': 'showPage',
            'search': 'showView' 
        }
    });

    var initialize = function () {
        var app_router, songPages;
        app_router = new AppRouter;

        vent.on('loadPage', function (id) {
            console.log('hit loaded page');
            var newPage = 'page/' + id;
            if(id < songPages && id >= 0 ) {
                app_router.navigate(newPage, true);
            } else {
                app_router.navigate('search', true);
            }

        })
        console.log('router file hit');
        app_router.on('route:showPage', function (id) {
            console.log('page rendered');
            var songs, collected, songM, start;
            songM = new SongM();
            songM.localStorage = new Backbone.LocalStorage("music");
            songM.localStorage.findAll().forEach(function (i) {
                collected = i;
            });
            songPages = Math.ceil(collected.music.length / 25); //10 pages
            start = id * 25;
            songs = collected.music.splice(start, 25); 
            var titles = {
                week: collected.week,
                year: collected.year,
                channel: collected. channel
            };
            var currentId = parseInt(id);
            if (page) {console.log('page is current'); console.log(page); page.remove(); }
            page = new PageV({model: songM, collection: songs, vent: vent, titles: titles, theId: currentId });

            page.render(id);
            //$("#Sirius").html(page.el);


        });

        app_router.on('route:showView', function () {
            console.log('search page loading...');
            var cur;
            var searchM = new SearchM();
            //if (search) {console.log(search); search.remove(); }
            search = new SearchV({model: searchM, vent: vent}); 
            //if(cur) {cur.stopListening(); cur.remove(); console.log('cur removed'); }

            search.render();
            //cur = search;
            vent.on('nextPage', printCons);
            function printCons () {
                console.log('changing pages');
                app_router.navigate('page/0', true);
            };          
        });

        Backbone.history.start();

    };

    return {
        initialize: initialize
    };
});

Here is the page with the page view:

  define([
  'jquery',
  'underscore',
  'backbone',
  'models/song',
  'collections/songs',
  'views/song',
  'text!templates/page.html',
  'text!templates/song.html'

], function($, _, Backbone, Song, Songs, SongV, PageT, SongT){ 

  var Page = Backbone.View.extend({

    el: $("#Sirius"),
    events: { 
      "click .prev": "previous",
      "click .next": "next"
    },

    previous: function () {
       this.options.vent.trigger('loadPage', this.options.theId - 1);
    },
    next: function () {
       this.options.vent.trigger('loadPage', this.options.theId + 1);
    },
    render: function () {
      var headings = this.options.titles; 
      var info = {
        week: headings.week,
        channel: headings.channel,
        year: headings.year
      }
      var pagetemp = _.template( PageT, info);
      this.$el.html( pagetemp );
      var songColl = this.collection;
      var songV = new SongV({collection: songColl});
      songV.render();

    }


  });
    return Page;
});

回答1:


This is happening because you re-use the same DOM element, #Sirius, each time you create a view. On initialization, Backbone views delegate DOM events to the their top level node, wrapped with $el.

On removing the view, cleanup is automatic, as the node is removed from the DOM. As you're replacing the HTML of the node without removing it, the event handlers are never removed, keeping your old views around responding to click events even after their content is no longer in the DOM.

There are a few ways to handle this.

  1. When rendering replaceable content, don't render directly into a DOM element. Rather, treat it as a container and render inside it. This way you can remove the view without affecting the original DOM. This is a common approach for page regions with swappable views.

    // somewhere in a higher scope, refer to the page.  Note that if you
    // don't need to do any cleanup (Events bindings, model cleanup, etc), you
    // don't *need* to keep this reference.
    var page;
    
    // in your show handler, replace the *contents* of the DOM element rather 
    // than creating a 2nd view attached to it.  This means removing the `el`
    // from the view prototype, as well.
    if (page) page.remove();
    page = new Page(...);
    page.render();
    $("#Sirius").html(page.el);
    
  2. Re-use the same view, updating its instance variables instead of replacing it. This makes particular sense for pagination, where you're iterating over a collection of content but the structure remains the same.

    // somewhere in a higher scope (or on app.page, etc)
    var page;
    
    // on the view, clean up the process of "updating" settings with a method,
    // something like:
    update: function (options) {
      this.model = options.model;
      this.collection = options.collection;
      this.options = options;
    }
    
    // then in your show handler, create a new view the first time, then just
    // update it rather than replacing it
    page || (page = new PageV());
    page.update(options);
    
  3. Clean up the delegated event bindings from the DOM element. I hesitate to add this as both 1 and 2 are more appropriate, but any of these approaches would probably work:

    // kludge level 1: reference the searchview and attempt to clean it
    if (page) {
      page.undelegateEvents();
      page.stopListening(); // if available
      // any other cleanup
    }
    page = new PageV(...);
    
    // kludge level 2: unbind all DOM events from the element
    $("#Sirius").off();
    var page = new PageV(...);
    
    // kludge level 3: replace the element in the DOM
    $("#Sirius").replaceWith('<div id="#Sirius" />');
    var page = new PageV(...);
    

Don't do #3. 1 or 2 are far more appropriate.



来源:https://stackoverflow.com/questions/19644259/how-to-correctly-remove-whatever-is-necessary-from-backbone-view

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!