backbone实例todos分析(二)view的应用

在上一篇文章中我们把todos这个实例的数据模型进行了简单的分析, 有关于数据模型的操作也都知道了。接着我们来看剩下的两个view的模型,以及它们对页面的操作。

首先要分析下,这个俩view是用来干嘛的。按照自己的想法,一个页面上的操作,直接用一个view来搞定不就行了吗,为何要用两个呢?

我觉得这就是新手和老手的主要区别之一,喜欢在一个方法里面搞定一切,随着时间的推移,再逐渐重构,让代码变得灵活可扩展。但既然我们拿到一个成熟的代码,就应该吸取其中的精华。

我觉得这里面的精华就是,将数据的展示和对数据的操作进行分离,也就是现在代码里面TodoView和AppView。前者的作用是展示数据模型中的数据到界面,并对数据本身进行管理;后者是对整体的一个控制,如所有数据的显示(调用TodoView),添加一个任务、统计多少完成任务等。

有了上面的分析,让我们来一起看下代码:

// 首先是创建一个全局的Todo的collection对象

  var Todos = new TodoList;

  // 先来看TodoView,作用是控制任务列表
  var TodoView = Backbone.View.extend({

    //下面这个标签的作用是,把template模板中获取到的html代码放到这标签中。
    tagName:  "li",

    // 获取一个任务条目的模板
    template: _.template($(‘#item-template‘).html()),

    // 为每一个任务条目绑定事件
    events: {
      "click .check"              : "toggleDone",
      "dblclick label.todo-content" : "edit",
      "click span.todo-destroy"   : "clear",
      "keypress .todo-input"      : "updateOnEnter",
      "blur .todo-input"          : "close"
    },

    //在初始化设置了todoview和todo的以一对一引用,这里我们可以把todoview看作是todo在界面的映射。
    initialize: function() {
      _.bindAll(this, ‘render‘, ‘close‘, ‘remove‘);
      this.model.bind(‘change‘, this.render);
      this.model.bind(‘destroy‘, this.remove);   //这个remove是view的中的方法,用来清除页面中的dom
    },

    // 渲染todo中的数据到 item-template 中,然后返回对自己的引用this
    render: function() {
      $(this.el).html(this.template(this.model.toJSON()));
      this.input = this.$(‘.todo-input‘);
      return this;
    },

    // 控制任务完成或者未完成
    toggleDone: function() {
      this.model.toggle();
    },

    // 修改任务条目的样式
    edit: function() {
      $(this.el).addClass("editing");
      this.input.focus();
    },

    // 关闭编辑界面,并把修改内容同步到界面
    close: function() {
      this.model.save({content: this.input.val()}); //会触发change事件
      $(this.el).removeClass("editing");
    },

    // 按下回车之后,关闭编辑界面
    updateOnEnter: function(e) {
      if (e.keyCode == 13) this.close();
    },

    // 移除对应条目,以及对应的数据对象
    clear: function() {
      this.model.clear();
    }
  });

//再来看AppView,功能是显示所有任务列表,显示整体的列表状态(如:完成多少,未完成多少)
//以及任务的添加。主要是整体上的一个控制
  var AppView = Backbone.View.extend({

    //绑定页面上主要的DOM节点
    el: $("#todoapp"),

    // 在底部显示的统计数据模板
    statsTemplate: _.template($(‘#stats-template‘).html()),

    // 绑定dom节点上的事件
    events: {
      "keypress #new-todo":  "createOnEnter",
      "keyup #new-todo":     "showTooltip",
      "click .todo-clear a": "clearCompleted",
      "click .mark-all-done": "toggleAllComplete"
    },

    //在初始化过程中,绑定事件到Todos上,当任务列表改变时会触发对应的事件。最后把存在localStorage中的数据取出来。
    initialize: function() {
      //下面这个是underscore库中的方法,用来绑定方法到目前的这个对象中,是为了在以后运行环境中调用当前对象的时候能够找到对象中的这些方法。
      _.bindAll(this, ‘addOne‘, ‘addAll‘, ‘render‘, ‘toggleAllComplete‘);
      this.input = this.$("#new-todo");
      this.allCheckbox = this.$(".mark-all-done")[0];
      Todos.bind(‘add‘,     this.addOne);
      Todos.bind(‘reset‘,   this.addAll);
      Todos.bind(‘all‘,     this.render);

      Todos.fetch();
    },

    // 更改当前任务列表的状态
    render: function() {
      var done = Todos.done().length;
      var remaining = Todos.remaining().length;

      this.$(‘#todo-stats‘).html(this.statsTemplate({
        total:      Todos.length,
        done:       done,
        remaining:  remaining
      }));

      //根据剩余多少未完成确定标记全部完成的checkbox的显示
      this.allCheckbox.checked = !remaining;
    },

    //添加一个任务到页面id为todo-list的div/ul中
    addOne: function(todo) {
      var view = new TodoView({model: todo});
      this.$("#todo-list").append(view.render().el);
    },

    // 把Todos中的所有数据渲染到页面,页面加载的时候用到
    addAll: function() {
      Todos.each(this.addOne);
    },

    //生成一个新Todo的所有属性的字典
    newAttributes: function() {
      return {
        content: this.input.val(),
        order:   Todos.nextOrder(),
        done:    false
      };
    },

    //创建一个任务的方法,使用backbone.collection的create方法。将数据保存到localStorage,这是一个html5的js库。需要浏览器支持html5才能用。
    createOnEnter: function(e) {
      if (e.keyCode != 13) return;
      Todos.create(this.newAttributes());  //创建一个对象之后会在backbone中动态调用Todos的add方法,该方法已绑定addOne。
      this.input.val(‘‘);
    },

    // 去掉所有已经完成的任务
    clearCompleted: function() {
      _.each(Todos.done(), function(todo){ todo.clear(); });
      return false;
    },

    //用户输入新任务的时候提示,延时1秒钟
    //处理逻辑是:首先获取隐藏的提示节点的引用,然后获取用户输入的值,
    //先判断是否有设置显示的延时,如果有则删除,然后再次设置,因为这个事件是按键的keyup时发生的,所以该方法会被连续调用。
    showTooltip: function(e) {
      var tooltip = this.$(".ui-tooltip-top");
      var val = this.input.val();
      tooltip.fadeOut();
      if (this.tooltipTimeout) clearTimeout(this.tooltipTimeout);
      if (val == ‘‘ || val == this.input.attr(‘placeholder‘)) return;
      var show = function(){ tooltip.show().fadeIn(); };
      this.tooltipTimeout = _.delay(show, 1000);
    },

    //处理页面点击标记全部完成按钮
    //处理逻辑:如果标记全部按钮已选,则所有都完成,如果未选,则所有的都未完成。
    toggleAllComplete: function () {
      var done = this.allCheckbox.checked;
      Todos.each(function (todo) { todo.save({‘done‘: done}); });
    }
  });

通过上面的代码,以及其中的注释,我们应该认识了其中的各个函数的作用。但是有一点没有说到的是template这个东西。

在前几篇的view介绍中我们已经认识过了简单的模板使用,以及变量参数的传递:

<script type="text/template" id="search_template">

        <label><%= search_label %></label>

        <input type="text" id="search_input" />

        <input type="button" id="search_button" value="Search" />

</script>

既然能定义变量,那么就能使用语法,如同django模板,那来看下带有语法的模板,也是上面的两个view用到的模板,我想这个是很好理解的。

<script type="text/template" id="item-template">

     <div class="todo <%= done ? ‘done‘ : ‘‘ %>">

       <div class="display">

         <input class="check" type="checkbox" <%= done ? ‘checked="checked"‘ : ‘‘ %> />

         <label class="todo-content"><%= content %></label>

         <span class="todo-destroy"></span>

       </div>

       <div class="edit">

         <input class="todo-input" type="text" value="<%= content %>" />

       </div>

     </div>

   </script>

   <script type="text/template" id="stats-template">

     <% if (total) { %>

       <span class="todo-count">

         <span class="number"><%= remaining %></span>

         <span class="word"><%= remaining == 1 ? ‘item‘ : ‘items‘ %></span> left.

       </span>

     <% } %>

     <% if (done) { %>

       <span class="todo-clear">

         <a href="#">

           Clear <span class="number-done"><%= done %></span>

           completed <span class="word-done"><%= done == 1 ? ‘item‘ : ‘items‘ %></span>

         </a>

       </span>

     <% } %>

   </script>

简单的语法,上面的那个对应TodoView。

这一篇文章就先到此为止,文章中我们了解到在todos这个实例中,view的使用,以及具体的TodoView和AppView中各个函数的作用,这意味着所有的肉和菜都已经放到你碗里了,下面就是如何吃下去的问题了。

下一篇我们一起来学习todos的整个流程。

时间: 2024-08-29 03:41:58

backbone实例todos分析(二)view的应用的相关文章

backbone实例todos分析(三)总结

在前两篇文章中,我们已经对这个todos的功能.数据模型以及各个模块的实现细节进行了分析,这篇文章我们要对前面的分析进行一个整合.前面我们说过,有了肉和菜,剩下的就是要怎么吃.我个人倾向于菜和肉一起吃,这样不会觉得腻 :-) 首先让我们来回顾一下我们分析的流程:先对页面功能进行了分析,然后又分析了数据模型,最后又对view的功能和代码进行了详解.你是不是觉得这个分析里面少了点什么?没错了,就知道经验丰富的你已经看出来了,这里面少了对于流程的分析. 所以从我的分析中可以看的出来,我是先对各个原材料

backbone实例todos分析(一)

todos的代码这里下载:https://github.com/documentcloud/backbone/ ?首先应该来看下功能,先看截图: ?从这个界面我们可以总结出来,这个Todos有哪些功能: 1.添加任务. 2.修改任务(包括内容,状态). 3.删除任务. 4.任务完成情况统计. 总体上就这四项功能. ?这个项目仅仅是在web端运行的,没有服务器进行支持,所以项目中使用了一个叫做backbone-localstorage的js库,用来把数据存储到前端. 因为backbone为mvc模

一些有用的javascript实例分析(二)

原文:一些有用的javascript实例分析(二) 1 5 求出数组中所有数字的和 2 window.onload = function () 3 { 4 var oBtn = document.getElementsByTagName("button")[0]; 5 var oInput = document.getElementsByTagName("input")[0] 6 var oStrong = document.getElementsByTagName

Android中的消息处理实例与分析

Android中的消息处理实例与分析 摘要 本文介绍了Android中的消息处理机制,给出了Android消息处理中的几个重点类Handler.Message.MessageQueue.Looper.Runnable.Thread的详细介绍,提供了两个消息处理的实例代码,并深入分析了使用Android消息机制应该遵循的几个原则. 阅读本文的收获 在具有java基础的情况下,Android的学习比较轻松,很多人在没有深刻了解Android消息处理机制的背景下,已经能够开发出可用的app,很多人开始

Android触摸屏事件派发机制详解与源码分析二(ViewGroup篇)

1 背景 还记得前一篇<Android触摸屏事件派发机制详解与源码分析一(View篇)>中关于透过源码继续进阶实例验证模块中存在的点击Button却触发了LinearLayout的事件疑惑吗?当时说了,在那一篇咱们只讨论View的触摸事件派发机制,这个疑惑留在了这一篇解释,也就是ViewGroup的事件派发机制. PS:阅读本篇前建议先查看前一篇<Android触摸屏事件派发机制详解与源码分析一(View篇)>,这一篇承接上一篇. 关于View与ViewGroup的区别在前一篇的A

netty 源码分析二

以服务端启动,接收客户端连接整个过程为例分析, 简略分为 五个过程: 1.NioServerSocketChannel 管道生成, 2.NioServerSocketChannel 管道完成初始化, 3.NioServerSocketChannel注册至Selector选择器, 4.NioServerSocketChannel管道绑定到指定端口,启动服务 5.NioServerSocketChannel接受客户端的连接,进行相应IO操作 Ps:netty内部过程远比这复杂,简略记录下方便以后回忆

Redis数据持久化机制AOF原理分析二

Redis数据持久化机制AOF原理分析二 分类: Redis 2014-01-12 15:36  737人阅读  评论(0)  收藏  举报 redis AOF rewrite 目录(?)[+] 本文所引用的源码全部来自Redis2.8.2版本. Redis AOF数据持久化机制的实现相关代码是redis.c, redis.h, aof.c, bio.c, rio.c, config.c 在阅读本文之前请先阅读Redis数据持久化机制AOF原理分析之配置详解文章,了解AOF相关参数的解析,文章链

Android Binder分析二:Natvie Service的注冊

这一章我们通过MediaPlayerService的注冊来说明怎样在Native层通过binder向ServiceManager注冊一个service,以及client怎样通过binder向ServiceManager获得一个service,并调用这个Service的方法. Native Service的注冊 这里以MediaPlayerService举例来说明怎样在Native层注冊Service,首先来看main_mediaservice.cpp的main方法: int main(int a

android原生browser分析(二)--界面篇

我们先看一张浏览器的主界面,上面标示浏览器界面各部分对应的类,这里是以平板上的界面为例.给张图是为了给大家一个直观的感觉. BrowserActivity是整个应用的主界面,在onCreate中创建了Controller对象,Controller对象是整个应用最重要的管理类,这个后面再说. @Override public void onCreate(Bundle icicle) { mController = createController(); } Controller的创建中新建了UI类