为什么是Backbone.Collection为第一个查看器显示了两次

Why is Backbone.Collection shown twice for the first viewer?

本文关键字:显示 两次 Backbone Collection 第一个 为什么      更新时间:2023-09-26

我试图通过修改Todo演示,在同一页面的两个不同查看器中显示相同的数据。任何数据更改都将反映在两个查看器中。该代码只适用于一个查看器,但在使用fetch重新加载页面后显示了我不想要的结果。todolist在第一个查看器中显示两次,其中一半的结果释放了绑定事件。有人能帮我吗?提前谢谢。

代码可以用jsfiddle播放。

HTML:

<div id="todoapp">

    <header>
      <h1>Todos</h1>
      <input class="new-todo" type="text" placeholder="What needs to be done?">
    </header>
    <section class="main">
      <input id=toggle-all class="toggle-all" type="checkbox">
      <label for="toggle-all">Mark all as complete</label>
      <ul class="todo-list"></ul>
    </section>
    <footer>
      <a class="clear-completed">Clear completed</a>
      <div class="todo-count"></div>
    </footer>
  </div>

  <div id="todoapp2">
    <header>
      <h1>Todos</h1>
      <input class="new-todo" type="text" placeholder="What needs to be done?">
    </header>
    <section class="main">
      <input id=toggle-all-2  class="toggle-all" type="checkbox">
      <label for="toggle-all-2">Mark all as complete</label>
      <ul class="todo-list"></ul>
    </section>
    <footer>
      <a class="clear-completed">Clear completed</a>
      <div class="todo-count"></div>
    </footer>
  </div>
  <div id="instructions">
    Double-click to edit a todo.
  </div>
   <!-- Templates -->
  <script type="text/template" id="item-template">
    <div class="view">
      <input class="toggle" type="checkbox" <%= done ? 'checked="checked"' : '' %> />
      <label><%- title %></label>
      <a class="destroy"> Destroy </a>
    </div>
    <input class="edit" type="text" value="<%- title %>" />
  </script>
  <script type="text/template" id="stats-template">
    <% if (done) { %>
      <a class="clear-completed">Clear <%= done %> completed <%= done == 1 ? 'item' : 'items' %></a>
    <% } %>
    <div class="todo-count"><b><%= remaining %></b> <%= remaining == 1 ? 'item' : 'items' %> left</div>
  </script>      

JS:

$(function () {
Todo = Backbone.Model.extend({
    defaults: function() {
      return {
        title: "empty todo...",
        order: Todos.nextOrder(),
        done: false
      };
    },
    toggle: function() {
      this.save({done: !this.get("done")});
    }
  });
TodoList = Backbone.Collection.extend({
    model: Todo,
    localStorage: new Backbone.LocalStorage("todos-backbone"),
    done: function() {
      return this.where({done: true});
    },
    remaining: function() {
      return this.where({done: false});
    },
    nextOrder: function() {
      if (!this.length) return 1;
      return this.last().get('order') + 1;
    },
    comparator: 'order'
  });

var Todos = new TodoList;

TodoView = Backbone.View.extend({
    tagName:  "li",
    template: _.template( $('#item-template').html() ),
    events: {
      "click .toggle"   : "toggleDone",
      "dblclick .view"  : "edit",
      "click a.destroy" : "clear",
      "keypress .edit"  : "updateOnEnter",
      "blur .edit"      : "close"
    },
    initialize: function() {
      this.listenTo(this.model, 'change', this.render);
      this.listenTo(this.model, 'destroy', this.remove);
    },
    render: function() {
      this.$el.html( this.template(this.model.toJSON()) );
      this.$el.toggleClass('done', this.model.get('done') );
      this.input = this.$('.edit');
        //alert(this.input.val() );

      return this;
    },
    toggleDone: function() {
      this.model.toggle();
    },
    edit: function() {
      this.$el.addClass("editing");
      this.input.focus();
    },
    close: function() {
      var value = this.input.val();
      if (!value) {
        this.clear();
      } else {
        this.model.save({title: value});
        this.$el.removeClass("editing");
      }
    },
    updateOnEnter: function(e) {
      if (e.keyCode == 13) this.close();
    },
    clear: function() {
      this.model.destroy();
    }
  });
  var AppView = Backbone.View.extend({
    //el: $("#todoapp"),
    statsTemplate: _.template($('#stats-template').html()),
    events: {
      "keypress .new-todo":  "createOnEnter",
      "click .clear-completed": "clearCompleted",
      "click .toggle-all": "toggleAllComplete"
    },
    initialize: function() {
      this.input = this.$(".new-todo");
      this.allCheckbox = this.$(".toggle-all")[0];
      this.listenTo(Todos, 'add', this.addOne);
      this.listenTo(Todos, 'reset', this.addAll);
      this.listenTo(Todos, 'all', this.render);
      this.footer = this.$('footer');
      this.main = this.$('.main');
        //Todos.fetch();
        Todos.fetch({reset:true});
    },
    render: function() {
      var done = Todos.done().length;
      var remaining = Todos.remaining().length;
      if (Todos.length) {
        this.main.show();
        this.footer.show();
        this.footer.html(this.statsTemplate({done: done, remaining: remaining}));
      } else {
        this.main.hide();
        this.footer.hide();
      }
      this.allCheckbox.checked = !remaining;
    },
    addOne: function(todo) {
      var view = new TodoView({model: todo});
        //console.log(view.render().el );
        //this.$(".todo-list").append( view.render().el );
        this.$(".todo-list").append( view.render().el );
        console.log( this.main.parent() );
    },
    addAll: function() {
      Todos.each(this.addOne, this);
    },
    createOnEnter: function(e) {
      if (e.keyCode != 13) return;
      if (!this.input.val()) return;
      Todos.create({title: this.input.val()});
      this.input.val('');
    },
    clearCompleted: function() {
      _.invoke(Todos.done(), 'destroy');
      return false;
    },
    toggleAllComplete: function () {
      var done = this.allCheckbox.checked;
      Todos.each(function (todo) { todo.save({'done': done}); });
    }
  });
  var app1 = new AppView({  el: $("#todoapp") });
  var app2 = new AppView({  el: $("#todoapp2") });
});

原因是两个视图只使用一个集合。

  • 最初创建app1,在初始化时获取集合,并且触发器事件仅由app1侦听
  • 现在创建了第二个视图,并再次获取集合,但现在在app1和app2中都侦听了由集合触发的重置事件,并且都进行了渲染

若要解决此问题,您只能在初始化两个视图后提取集合一次。

工作小提琴