backbone实例Todo源码赏析

作者:zccst

Todo实例花了我两天多的事件,为了下次再忘记,决定记录下来:

$(function(){
/******************** 模型-集合 ********************/
  var Todo = Backbone.Model.extend({
    defaults:function(){//defaults是函数
      return {title:'apple', order:Todos.nextOrder(), done:false};
    },
    initialize:function(){
      if(!this.get("title")){ this.set({title:this.defaults().title}); }
    },
    validate:function(){
      //console.log(this);//alert('validate');
    },
    toggle:function(){//每次取反
      //model变,那么Todos也变,触发Collection的all事件(不是reset)
      this.save({done:!this.get('done')});
    }
  });

  var TodoList = Backbone.Collection.extend({
    model:Todo,
    localStorage:new Backbone.LocalStorage("todos-backbone-my"),//存储
    //默认没有,如果定义了,则被用来维护集合在正确的顺序
    comparator:function(todo){
      return todo.get("order");//使用order作为排序基准。order
    },
    done:function(){//选择done字段为true的集合
      //filter是underscore提供的
      return this.filter(function(todo){return todo.get("done");});
    },
    remaining:function(){
      //underscore提供,排除this中的this.done()元素列表
      return this.without.apply(this, this.done());
    },
    nextOrder:function(){//返回该元素的下一个元素序号
      if(!this.length) return 1;
      //underscore提供,获取最后一个元素的order值再加1
      return this.last().get("order") + 1;
    }
  });
  var Todos = new TodoList;//创建一个名叫Todos的全局集合列表

/******************** 视图 ********************/
  var TodoView = Backbone.View.extend({
    tagName : "li",//el:"todo-list",
    template:_.template($("#item-template").html()),
    events:{
      'click .toggle'  : 'toggleDone',   //点击切换,选中状态
      'dbclick .view'  : 'edit',         //双击编辑
      'blur .edit'     : 'close',        //双击编辑后,失去焦点
      'keypress .edit' : 'updateOnEnter',//双击编辑后,按enter键 编辑成功
      'click a.destroy': 'clear'         //点击删除
    },
    initialize:function(){//model也就两个事件
      this.listenTo(this.model, 'change', this.render);
      this.listenTo(this.model, 'destroy', this.remove);
    },
    render:function(){
      //console.log(this.model);
      this.$el.html(this.template(this.model.toJSON()));
      this.$el.toggleClass("done",this.model.get("done"));
      this.input = $(".edit");//始终指向当前被点击的行
      return this;
    },
    toggleDone:function(){//点击切换选中状态
      this.model.toggle();
    },
    edit:function(){//双击编辑
      this.$el.addClass("editing");
      this.input.focus();
    },
    close:function(){//双击编辑后,input失去焦点
      var value = this.input.val();
      if(!value){
        this.clear();
      }else{
        this.model.save({title:value});//直接save,不需要set啊
        this.$el.removeClass("editing");
      }
    },
    updateOnEnter:function(e){//双击编辑后,按enter键 编辑成功
      if(e.keyCode == 13) this.close();
    },
    clear:function(){//点击删除
      this.model.destroy();//先触发destroy,后触发change事件
    }
  });

  var AppView = Backbone.View.extend({
    //el:"#todoapp",el表示element,如果是id,需要#ID
    el:$("#todoapp"),
    statsTemplate : _.template($("#stats-template").html()),
    events:{//events是对象
      'keypress #new-todo'     : 'createOnEnter',     //创建新的
      'click #toggle-all'      : 'toggleAllComplete', //全选
      'click #clear-completed' : 'clearCompleted'     //清空
    },
    initialize:function(){
      this.input = this.$("#new-todo");
      this.allCheckbox = this.$("#toggle-all")[0];
      //触发add事件的方法 :create, fetch, add, set
      this.listenTo(Todos, 'add', this.addOne);  
      //this.listenTo(Todos, 'reset', this.addAll);//触发reset :reset
      //只控制了footer模板,查询计算已选和未选个数
      //this.listenTo(Todos, 'all', this.render);  
      this.listenTo(Todos, 'change', this.render);
      this.listenTo(Todos, 'add', this.render);
      this.listenTo(Todos, 'remove', this.render);
      this.footer = this.$("footer");
      this.main   = this.$("#main");
      //化学效应,先驻足半小时,过一遍backbone的事件监听流程
      Todos.fetch();//没触发change,reset,remove事件,触发了add事件。{reset:true}触发reset事件
    },
    render:function(){//更新当前任务列表的状态
      var done = Todos.done().length;           //已选中的集合
      var remaining = Todos.remaining().length; //未选中的集合
      if( Todos.length ){                       //Todos.length全部的集合
        this.main.show();
        this.footer.show();
        this.footer.html(this.statsTemplate({done:done,remaining:remaining}));
      }else{
        this.main.hide();
        this.footer.hide();
      }
      //console.log(done,remaining);
      this.allCheckbox.checked = !remaining;
    },
    createOnEnter:function(e){//在events中绑定,所以参数是event
      if(e.keyCode != 13) return;
      if(!this.input.val()) return;
      //触发集合的add事件(绑定了addOne方法)
      Todos.create({'title':this.input.val()});
      this.input.val("");
    },
    toggleAllComplete:function(){
      var done = this.allCheckbox.checked;
      Todos.each(function(todo){ todo.save({'done':done}); });
    },
    clearCompleted:function(){
      //清除所有被选中的(done:true的)
      //_.invoke(Todos.done(), 'destroy');
      var arr = Todos.done();for(var item in arr){ arr[item].destroy();};//替换
      return false;
    },
    addOne:function(todo){//在initialize中绑定,所以参数是model
      var view = new TodoView({model:todo});
      //Todo的render方法必须主动调
      this.$("#todo-list").append(view.render().el);
    },
    addAll:function(){
      Todos.each(this.addOne, this);
    }
  });
var AppView = new AppView();
});

批注:AppView中initialize的add,change,remove加起来基本可以相当于all事件了。

reset的使用场景:

1,调用fetch()方法时加了{reset:true}时触发reset事件

2,直接reset({...});时触发reset事件

<div id="todoapp">
    <header>
        <h1>Todos演示</h1>
        <input id="new-todo" type="text" placeholder="请输入您想做的事情?">
    </header>

    <section id="main">
        <!--for 属性规定 label 与哪个表单元素绑定-->
        <input id="toggle-all" type="checkbox">
        <label for="toggle-all">全选</label>

        <ul id="todo-list"></ul>
    </section>

    <footer>
      <a id="clear-completed">全部清空</a>
      <div id="todo-count"></div>
    </footer>
</div>

<div id="instructions">
    双击编辑一个todo.
</div>

<div id="credits">
    由<a href="http://www.sogou.com">某某</a>创建。
    <br />Rewritten by: <a href="http://addyosmani.github.com/todomvc">TodoMVC</a>.
</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"></a>
    </div>
    <input class="edit" type="text" value="<%- title %>" />
</script>

<!-- 底部模板 -->
<script type="text/template" id="stats-template">
    <% if (done) { %>
      <a id="clear-completed"> 有 <%= done %> 个被选中,立即清除 </a>
    <% } %>
    <div class="todo-count">还有 <b><%= remaining %></b> 个未被选中</div>
</script>

如果您觉得本文的内容对您的学习有所帮助,您可以微信:

相关推荐