zoukankan      html  css  js  c++  java
  • backbone-todo案例分析

    todo案例可以到这个地址下载 https://github.com/jashkenas/backbone

    添加数据后

    todo案例不涉及Router,仅有Model、Collection、View的应用,但这个案例对于这三个对象的使用真是已经到了炉火纯青的地步,我看了两天(囧),也只是看懂了表面一层,至于更深一层的思想就太模糊、若有若无了。还需努力啊!

    todo案例的核心在todos.js文件里,我分析的也是todos.js。

    首先是数据模型Model

     1 var Todo = Backbone.Model.extend({
     2     defaults : function() {                //看好多人都用function return这种方式,但不知道好处在哪
     3         return {
     4             title : "empty todo ...",
     5             order : Todos.nextOrder(),    //要有Todos这个全局变量才行
     6             done : false
     7         }
     8     },
     9     //切换状态
    10     toggle : function(){
    11         this.save({done : !this.get("done")});    //注意:我在这里犯过2次错误,把save写成了set,导致数据保存不上。
    12     },                                            //更新数据要使用 save,不是其他。RESTful风格就是这样。
    13 });

    这个Model和java中的bean何其相似啊!属性有:title、order、done,方法有 toggle,这个方法也仅仅是更新其中的一个属性而已。

    然后是集合Collection

     1 var TodoList = Backbone.Collection.extend({
     2     //model
     3     model : Todo,    //如java中List集合中的泛型,表面这是一个Todo类型的集合(即集合存储Todo对象)
     4     
     5     //本地存储,操作的数据都存储在localStorage里面,没有后端
     6     localStorage : new Backbone.LocalStorage("todos-backbone"),    
     7     //The app uses a LocalStorage adapter to transparently save all of your todos within your browser, instead of sending them to a server
     8     
     9     //获取状态为 done:true 的Todo对象数组
    10     done : function(){
    11         return this.where({done : true});
    12     },
    13     //获取状态为 done:false 的Todo对象数组
    14     remaining : function(){
    15         return this.where({done : false});
    16     },
    17     
    18     //生成下一个Todo对象的编号
    19     nextOrder : function(){
    20         if(!this.length){    //this.length 获取集合中model的数量
    21             return 1;
    22         }
    23         return this.last().get("order") + 1;
    24     },
    25     
    26     comparator : "order" , //按照 order 进行排序
    27     
    28 });

    总体来说,这个TodoList集合用来存储Todo对象到localStorage里并为他们编号,同时获取不同状态下的Todo对象。

    小结:Model 和 Collection 都是用来存储数据的,里面的操作也都是对数据的操作,不与页面逻辑混合。

    接下来是TodoView

     1 var TodoView = Backbone.View.extend({
     2     
     3     tagName : "li",    
     4     
     5     template : _.template($("#item-template").html()),        //页面模板,_.template是underScore的函数
     6     
     7     events : {                                                //DOM事件
     8         "click .toggle"     : "toggleDone",
     9         "dblclick .view"     : "edit",
    10         "click a.destroy"    : "clear",
    11         "keypress .edit"    : "updateOnEnter",
    12         "blur .edit"        : "close",
    13     },
    14     
    15     initialize : function(){                                //构造函数        
    16         //监听Collection的变化并作出反应
    17         this.listenTo(this.model, "change", this.render);    //model属性改变时会触发该事件
    18         this.listenTo(this.model, "destroy", this.remove);   //销毁model时,触发该事件。this.remove()移除一个视图(即销毁model的同时移除view)
    19     },
    20     
    21     //渲染
    22     render : function(){
    23             // el是tagName
    24         this.$el.html(this.template(this.model.toJSON()));    //将model的属性作为参数传入模板生成html代码,同时填充到el元素里
    25         
    26         this.$el.toggleClass("done", this.model.get("done"));//根据model的属性值来判断元素的类
    27         
    28         this.input = this.$(".edit");        //注意这个写法
    29         
    30         return this;        //为了链式调用
    31     },
    32     
    33     /*** DOM事件处理函数 ***/
    34     
    35     //切换状态
    36     toggleDone : function(){
    37         this.model.toggle();
    38     },
    39     
    40     edit : function(){
    41         this.$el.addClass("editing");
    42         this.input.focus();
    43     },
    44     
    45     close : function(){
    46         var value = this.input.val();
    47         if(!value){
    48             this.clear();
    49         }else{
    50             this.model.save({title : value});
    51             this.$el.removeClass("editing");
    52         }
    53         
    54     },
    55     
    56     updateOnEnter : function(){
    57         if(e.keyCode === 13){
    58             this.close();
    59         }
    60     },
    61     
    62     clear : function(){
    63         this.model.destroy();    //销毁model,触发destroy事件
    64     },
    65 
    66 });

    每个View视图都有一个DOM元素,这个DOM元素就是el,这个view里所有关于页面的操作都必须在el下(注意,在view操作el的父元素或同级元素都将无效),如果没有指定el,那么el将从tagName、className、id创建,如果也没有,那么el就是一个空的div。

    TodoView视图表示的就是ul中的一个li,如

    写view的思路:

      1、 el,这是一个view的根元素

      2、template,这是view的模板,该模板将在render插入到el元素中

      3、render,渲染页面,负责将model的数据显示在html模板中

      4、DOM事件,想象该view哪些地方需要和用户进行交互,然后在events中绑定事件

      5、完成DOM事件处理函数

      6、观察哪些操作造成了哪些数据的变化,为这些数据绑定事件,当数据变化时更新数据并再次渲染页面。

    比如上面的TodoView,el是li,模板渲染需要Todo数据,在这个li里,要有

    • 单击checkbox,更新model数据
    • 单击li后面的X号,删除model
    • 双击li,变成编辑状态
      • 回车或者焦点离开更新model数据

    我自己的感觉,前5个步骤都比较简单,就是第6步用起来有些生涩。不过多几次就好了!

    最后是AppView

     1 var AppView = Backbone.View.extend({
     2     
     3     el : $("#todoapp"),
     4     
     5     statsTemplate : _.template($("#stats-template").html()),
     6     
     7     events : {
     8         "keypress #new-todo"    : "createOnEnter",
     9         "click #clear-completed": "clearCompleted",
    10         "click #toggle-all"        : "toggleAllComplete",
    11     },
    12     
    13     initialize : function(){
    14         this.input = this.$("#new-todo");
    15         this.allCheckbox = this.$("#toggle-all")[0];
    16         
    17         this.listenTo(Todos, "add", this.addOne);     //当有Todo对象添加到Todos集合触发该事件(添加一个model就添加一个view)
    18     //    this.listenTo(Todos, "reset", this.addAll); //
    19         this.listenTo(Todos, "all", this.render);     //任何事件的发生都会调用该回调函数this.render
    20         
    21         this.footer = this.$("footer");
    22         this.main = this.$("#main");
    23         
    24         Todos.fetch();    //拉取所有的Model对象并保存到Todos集合中,会触发Todos的add事件
    25     },
    26     
    27     render : function(){
    28         var done = Todos.done().length;
    29         var remaining = Todos.remaining().length;
    30         
    31         if(Todos.length){
    32             this.main.show();
    33             this.footer.show();
    34             this.footer.html(this.statsTemplate({done : done, remaining : remaining}));
    35         }else{
    36             this.main.hide();
    37             this.footer.hide();
    38         }
    39         this.allCheckbox.checked = !remaining;
    40     },
    41     
    42     addOne : function(todo){
    43         var view = new TodoView({model : todo});
    44         view.render();
    45         this.$("#todo-list").append(view.el);
    46     },
    47 /**    
    48     addAll : function(){
    49         //backbone代理了Underscore的一些方法
    50         Todos.each(this.addOne, this);
    51     },
    52 **/    
    53     createOnEnter : function(e){
    54         if(e.keyCode != 13){
    55             return;
    56         }
    57         if(!this.input.val()){
    58             return;
    59         }
    60         Todos.create({title : this.input.val()});
    61         this.input.val("");
    62     },
    63     
    64     clearCompleted : function(){
    65         //_.invoke(Todos.done(), "destroy");    //遍历Todos.done(),每个元素都 todo.destroy();
    66         
    67         _.each(Todos.done(),function(todo){
    68             todo.destroy();
    69         });
    70         return false;
    71     },
    72     
    73     toggleAllComplete : function(){
    74         var done = this.allCheckbox.checked;
    75         Todos.each(function(todo){
    76             todo.save({
    77                 'done' : done,
    78             });
    79         });
    80     },
    81     
    82 });

    这个view表示整个页面,比较复杂(自己觉得)。

    将它从上到下拆分开来,就变的简单了。

    最上面是头,一个标题和一个输入框,输入框有一个DOM事件keypress。

    1 <header>
    2  <h1>Todos</h1>
    3  <input id="new-todo" type="text" placeholder="What needs to be done?">
    4</header>

    中间是身体,一个复选框一个label提示信息一个ul列表,复选框有一个DOM事件,而ul列表中li子项则是作为TodoView存在的。

    1 <section id="main">
    2     <input id="toggle-all" type="checkbox">
    3     <label for="toggle-all">Mark all as complete</label>
    4     <ul id="todo-list">
    5     </ul>
    6 </section>

    最下是脚,一个div显示信息一个超链接,超链接有一个DOM事件,清除完成的任务。

    1 <footer>
    2     <a id="clear-completed">Clear completed</a>
    3     <div id="todo-count"></div>
    4 </footer>

    这样在去看AppView就简单多了。

    关键还是内在的思想,可以偶还没看明白! >_<

    ---------------------------------

    AppView的数据核心就是 TodoList,TodoList对象的变化要同步到AppView,所有要监听TodoList对象。

  • 相关阅读:
    PLSQL Developer使用技巧整理
    PLSQL DEVELOPER 使用的一些技巧【转】 .
    MYEclipse Available Memory is low 警告 解决方法
    myeclipse安装svn插件的多种方式
    MySql的存储过程和触发器
    springmvc学习及源码地址
    spring源码下载链接
    struts2源码下载链接
    个人总结的常用java,anroid网站
    Java生成扫描可以生成手机号名片的二维码
  • 原文地址:https://www.cnblogs.com/lhat/p/5152237.html
Copyright © 2011-2022 走看看