大凡出名的MVC,MVVM框架都有todo例子,我们也搞一下看看avalon是否这么便宜。

我们先从react的todo例子中扒一下HTML与CSS用用。

  1. <!doctype html>
  2. <html lang="en" data-framework="react">
  3. <head>
  4. <meta charset="utf-8">
  5. <title>React • TodoMVC</title>
  6. <link rel="stylesheet" href="bower_components/todomvc-common/base.css">
  7. </head>
  8. <body>
  9. <section id="todoapp"></section>
  10. <footer id="info">
  11. <p>Double-click to edit a todo</p>
  12. <p>Created by <a href="http://github.com/petehunt/">petehunt</a></p>
  13. <p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
  14. </footer>
  15.  
  16. <script src="bower_components/todomvc-common/base.js"></script>
  17. <script src="bower_components/react/react-with-addons.js"></script>
  18. <script src="bower_components/react/JSXTransformer.js"></script>
  19. <script src="bower_components/director/build/director.js"></script>
  20.  
  21. <script src="js/utils.js"></script>
  22. <script src="js/todoModel.js"></script>
  23. <!-- jsx is an optional syntactic sugar that transforms methods in React's
  24. `render` into an HTML-looking format. Since the two models above are
  25. unrelated to React, we didn't need those transforms. -->
  26. <script type="text/jsx" src="js/todoItem.jsx"></script>
  27. <script type="text/jsx" src="js/footer.jsx"></script>
  28. <script type="text/jsx" src="js/app.jsx"></script>
  29. </body>
  30. </html>

改成下面这样

  1. <!doctype html>
  2. <html lang="cn">
  3. <head>
  4. <meta charset="utf-8">
  5. <title>avalon • TodoMVC</title>
  6. <link rel="stylesheet" href="http://todomvc.com/architecture-examples/react/bower_components/todomvc-common/base.css">
  7. </head>
  8. <body>
  9. <section id="todoapp"></section>
  10. <footer id="info">
  11. <p>Double-click to edit a todo</p>
  12. <p>Created by <a href="https://github.com/RubyLouvre/avalon">司徒正美</a></p>
  13. <p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
  14. </footer>
  15. </body>
  16. </html>

由于用了许多新标签与CSS3,因此肯定在旧式IE下一塌糊涂,大家需要在chrome下浏览。

我们添加一些avalon东西,改一下版权什么的

  1. <!doctype html>
  2. <html lang="cn">
  3. <head>
  4. <meta charset="utf-8">
  5. <title>avalon • TodoMVC</title>
  6. <link rel="stylesheet" href="http://todomvc.com/architecture-examples/react/bower_components/todomvc-common/base.css">
  7. <script src="avalon.js"></script>
  8. <style>
  9. .ms-controller{
  10. visibility: hidden;
  11. }
  12. </style>
  13. <script>
  14.  
  15. var model = avalon.define({
  16. $id: "todo"
  17. })
  18.  
  19. </script>
  20. </head>
  21. <body ms-controller="todo" class="ms-controller">
  22. <section id="todoapp">
  23. <header id="header">
  24. <h1>todos</h1>
  25. <form id="todo-form" autocomplete="off">
  26. <input id="new-todo" placeholder="What needs to be done?" autofocus>
  27. </form>
  28. </header>
  29.  
  30. </section>
  31. <footer id="info">
  32. <p>Double-click to edit a todo</p>
  33. <p>Created by <a href="https://github.com/RubyLouvre/avalon">司徒正美</a></p>
  34. <p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
  35. </footer>
  36. </body>
  37. </html>

好了,有模有样了。我们添加一些实际功能了。todoMVC主要是演示往一个数组里添加东西,然后可以编辑删除它。那么我们就先得有一个变量来保存要添加的东西,及一个可以往里面添加的东西的数组。我把它们命名为newTodo与todos。添加是一个用户行为,因此我们需要一个addTodo的提交回调。

  1. <!doctype html>
  2. <html lang="cn">
  3. <head>
  4. <meta charset="utf-8">
  5. <title>avalon • TodoMVC</title>
  6. <link rel="stylesheet" href="http://todomvc.com/architecture-examples/react/bower_components/todomvc-common/base.css">
  7. <script src="avalon.js"></script>
  8. <style>
  9. .ms-controller{
  10. visibility: hidden;
  11. }
  12. </style>
  13. <script>
  14.  
  15. var model = avalon.define({
  16. $id: "todo",
  17. newTodo: "",
  18. todos: [],
  19. addTodo: function(e) {
  20. e.preventDefault()//阻止页面刷新
  21. var newTodo = model.newTodo.trim()
  22. if (!newTodo.length) {
  23. return
  24. }
  25. model.todos.push({
  26. title: newTodo,
  27. completed: false
  28. });
  29. model.newTodo = ""//清空内容
  30. }
  31.  
  32. })
  33.  
  34. </script>
  35. </head>
  36. <body ms-controller="todo" class="ms-controller">
  37. <section id="todoapp">
  38. <header id="header">
  39. <h1>todos</h1>
  40. <form id="todo-form" ms-submit="addTodo" autocomplete="off">
  41. <input id="new-todo" ms-duplex="newTodo" placeholder="What needs to be done?" autofocus>
  42. </form>
  43. </header>
  44. <section id="main" ms-visible="todos.size()" >
  45. <input id="toggle-all" type="checkbox" >
  46. <label for="toggle-all">Mark all as complete</label>
  47. <ul id="todo-list">
  48. <li ms-repeat-todo="todos" ms-class="completed: todo.completed" >
  49. <div class="view">
  50. <input class="toggle" type="checkbox" >
  51. <label >{{todo.title}}</label>
  52. <button class="destroy" ></button>
  53. </div>
  54. <form>
  55. <input class="edit" >
  56. </form>
  57. </li>
  58. </ul>
  59. </section>
  60.  
  61. </section>
  62. <footer id="info">
  63. <p>Double-click to edit a todo</p>
  64. <p>Created by <a href="https://github.com/RubyLouvre/avalon">司徒正美</a></p>
  65. <p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
  66. </footer>
  67. </body>
  68. </html>

我们看到ms-visible是对应todos.size()而不是todos.length,那是因为数组的原生属性length无法hack进去,因此实现不了监控功能,实现不了监控也无法双向绑定了。

我们再来看如何实现编辑删除。编辑需要一个双击事件,并且一个时期内只能有一个todo处于编辑状态。这里我使用editingIndex属性,它是保存正在编辑的元素的索引值,如果不想编辑,将它置为NaN就行了。比如那个文本域绑定一个失去焦点事件,那个回调就是这样干。移除元素,直接使用ms-repeat绑定临时生成的$remove方法。

  1. editingIndex: NaN,
  2. editTodo: function($index) {
  3. model.editingIndex = $index
  4. //为了用户体验,有时不得不写一些DOM处理
  5. var el = this.parentNode.parentNode
  6. setTimeout(function() {//让光标定位于文本之后
  7. var input = el.querySelector("input.edit")
  8. input.focus()
  9. input.value = input.value
  10. })
  11. },
  12. doneEditing: function() {//还原
  13. model.editingIndex = NaN
  14. },
  1. <ul id="todo-list">
  2. <li ms-repeat-todo="todos" ms-class="completed: todo.completed" ms-class-1="editing: $index === editingIndex">
  3. <div class="view">
  4. <input class="toggle" type="checkbox" >
  5. <label ms-dblclick="editTodo($index)">{{todo.title}}</label>
  6. <button class="destroy" ms-click="$remove"></button>
  7. </div>
  8. <form>
  9. <input class="edit" ms-duplex="todo.title" ms-blur="doneEditing" >
  10. </form>
  11. </li>
  12. </ul>

接着来做全选非选功能,我们需要一个变量来保存全选状态,然后监听它来同步下面UI列表的checkbox的勾选情况,这个使用$watch回调实现就行了。但UI列表的每个元素的complete是否打勾也会影响到上方的全选checkbox,这时不可能为每个元素添加$watch回调,我们改用data-duplex-changed回调checkOne实现。

  1. // 下面几行在define函数里
  2. allChecked: false,
  3. checkOne: function() {//点击UI列表的checkbox时
  4. model.$unwatch() //阻止下面allChecked的$watch回调触发
  5. model.allChecked = model.todos.every(function(val) {
  6. return val.completed
  7. })
  8. model.$watch()
  9.  
  10. },
  11. //这是在define函数外
  12. model.$watch("allChecked", function(completed) {//点击上方checkbox时
  13. model.todos.forEach(function(todo) {
  14. todo.completed = completed
  15. })
  16. })
  1. <section id="main" ms-visible="todos.size()" >
  2. <input id="toggle-all" type="checkbox" ms-duplex-radio="allChecked">
  3. <label for="toggle-all">Mark all as complete</label>
  4. <ul id="todo-list">
  5. <li ms-repeat-todo="todos" ms-class="completed: todo.completed" ms-class-1="editing: $index === editingIndex">
  6. <div class="view">
  7. <input class="toggle" type="checkbox" ms-duplex-radio="todo.completed" data-duplex-changed="checkOne">
  8. <label ms-dblclick="editTodo($index)">{{todo.title}}</label>
  9. <button class="destroy" ms-click="$remove"></button>
  10. </div>
  11. <form>
  12. <input class="edit" ms-duplex="todo.title" ms-blur="doneEditing" >
  13. </form>
  14. </li>
  15. </ul>
  16. </section>

接着我们做页脚部分,这是有几个按钮,一个用来删除所有选中的todo,几个是用链接摸拟的,用于切换状态,还有一段描述文本。它们涉及到一个数组,用于装载三个状态值,一个表示当前状态的变量,还有两个数值remainingCount(当前有多少个todo没有被选中),completedCount(当前有多少个todo被选中),还有一个事件回调,用于移除removeCompleted。

难点有两处。第一处是remainingCount与completedCount的计算,没有什么智能的计算方法,需要我们自己写一个方法,当数组的长度发生变化,用户点击了各种checkbox时触发。

第二个是状态值的表示,todoMVC要求它们首字母大写,这里我引入一个自定义过滤器capitalize实现它。

  1. <!doctype html>
  2. <html lang="cn">
  3. <head>
  4. <meta charset="utf-8">
  5. <title>avalon • TodoMVC</title>
  6. <link rel="stylesheet" href="http://todomvc.com/architecture-examples/react/bower_components/todomvc-common/base.css">
  7. <script src="avalon.js"></script>
  8. <style>
  9. .ms-controller{
  10. visibility: hidden;
  11. }
  12. </style>
  13. <script>
  14. avalon.filters.capitalize = function(a) {
  15. return a.charAt(0).toUpperCase() + a.slice(1)
  16. }
  17. var model = avalon.define({
  18. $id: "todo",
  19. newTodo: "",
  20. todos: [],
  21. addTodo: function(e) {
  22. e.preventDefault()//阻止页面刷新
  23. var newTodo = model.newTodo.trim()
  24. if (!newTodo.length) {
  25. return
  26. }
  27. model.todos.push({
  28. title: newTodo,
  29. completed: false
  30. });
  31. model.newTodo = ""//清空内容
  32. },
  33. editingIndex: NaN,
  34. editTodo: function($index) {
  35. model.editingIndex = $index
  36. //为了用户体验,有时不得不写一些DOM处理
  37. var el = this.parentNode.parentNode
  38. setTimeout(function() {//让光标定位于文本之后
  39. var input = el.querySelector("input.edit")
  40. input.focus()
  41. input.value = input.value
  42. })
  43. },
  44. doneEditing: function() {//还原
  45. model.editingIndex = NaN
  46. },
  47. allChecked: false,
  48. checkOne: function() {//点击UI列表的checkbox时
  49. model.$unwatch()
  50. model.allChecked = model.todos.every(function(val) {
  51. return val.completed
  52. })
  53. model.$watch()
  54. updateCount()
  55. },
  56. state: "all",
  57. status: ["all", "active", "completed"],
  58. remainingCount: 0,
  59. completedCount: 0,
  60. removeCompleted: function() {
  61. model.todos.removeAll(function(el) {
  62. return el.completed
  63. })
  64. }
  65. })
  66.  
  67. function updateCount() {
  68. model.remainingCount = model.todos.filter(function(el) {
  69. return el.completed === false
  70. }).length
  71. model.completedCount = model.todos.length - model.remainingCount;
  72. }
  73.  
  74. model.$watch("allChecked", function(completed) {//点击上方checkbox时
  75. model.todos.forEach(function(todo) {
  76. todo.completed = completed
  77. })
  78. updateCount()
  79. })
  80.  
  81. model.todos.$watch("length", updateCount)
  82.  
  83. </script>
  84. </head>
  85. <body ms-controller="todo" class="ms-controller">
  86. <section id="todoapp">
  87. <header id="header">
  88. <h1>todos</h1>
  89. <form id="todo-form" ms-submit="addTodo" autocomplete="off">
  90. <input id="new-todo" ms-duplex="newTodo" placeholder="What needs to be done?" autofocus>
  91. </form>
  92. </header>
  93. <section id="main" ms-visible="todos.size()" >
  94. <input id="toggle-all" type="checkbox" ms-duplex-radio="allChecked">
  95. <label for="toggle-all">Mark all as complete</label>
  96. <ul id="todo-list">
  97. <li ms-repeat-todo="todos" ms-class="completed: todo.completed" ms-class-1="editing: $index === editingIndex">
  98. <div class="view">
  99. <input class="toggle" type="checkbox" ms-duplex-radio="todo.completed" data-duplex-changed="checkOne">
  100. <label ms-dblclick="editTodo($index)">{{todo.title}}</label>
  101. <button class="destroy" ms-click="$remove"></button>
  102. </div>
  103. <form action="javascript:void(0)" ms-submit="doneEditing">
  104. <input class="edit" ms-duplex="todo.title" ms-blur="doneEditing" >
  105. </form>
  106. </li>
  107. </ul>
  108. </section>
  109. <footer id="footer" ms-visible="todos.size()">
  110. <span id="todo-count">
  111. <strong >{{remainingCount}}</strong>
  112. item{{remainingCount>1 ? "s" : ""}} left
  113. </span>
  114. <ul id="filters">
  115. <li ms-repeat="status">
  116. <a ms-class="selected: state == el" href="#/{{el}}" >{{ el | capitalize }}</a>
  117. </li>
  118. </ul>
  119. <button id="clear-completed" ms-visible="completedCount" ms-click="removeCompleted">
  120. Clear completed ({{completedCount}})
  121. </button>
  122. </footer>
  123. </section>
  124. <footer id="info">
  125. <p>Double-click to edit a todo</p>
  126. <p>Created by <a href="https://github.com/RubyLouvre/avalon">司徒正美</a></p>
  127. <p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
  128. </footer>
  129. </body>
  130. </html>

这就完了,当然有人可能问如何切换状态,在todoMVC里是使用路由系统实现,当我完善了自带的路由系统时再补上吧。总结一下,用avalon来实现todoMVC,所有JS代码与HTML行数是最少的。JS代码包括avalon库与用户写的代码。像angular,backbone, polymer虽然吹得这么响,它们在实现todoMVC时有许多部分是非常不直观的,冗长的,唯有avalon是最好的。

迷你MVVM框架 avalonjs 学习教程18、一步步做一个todoMVC的更多相关文章

  1. 迷你MVVM框架 avalonjs 学习教程19、avalon历史回顾

    avalon最早发布于2012.09.15,当时还只是mass Framework的一个模块,当时为了解决视图与JS代码的分耦,参考knockout开发出来. 它的依赖收集机制,视图扫描,绑定的命名d ...

  2. 迷你MVVM框架 avalonjs 学习教程11、循环操作

    avalon是通过ms-repeat实现对一组数据的批量输出.这一组数据可以是一个数组,也可以是一个哈希(或叫对象).我们先从数组说起吧. 第二节就说,凡是定义在VM中的数组,如果没有以$开头或者没放 ...

  3. 迷你MVVM框架 avalonjs 学习教程4、数据填充

    MVVM是前端的究极解决方案,你们可能用过jQuery,但那个写的代码不易维护:你们可以听过说requirejs与seajs,传说中的模块开发,加载器,但它们的最终目标是打包:你们可能听过unders ...

  4. 迷你MVVM框架 avalonjs 学习教程3、绑定属性与扫描机制

    在MVVM框架中,你都会看到页面定了许多奇怪的属性,比如knockout的data-☆,angular的ng-☆,avalon的ms-☆,此外还有一些只写文本节点上的双花括号,它们统称为指令.ms-☆ ...

  5. 迷你MVVM框架 avalonjs 学习教程1、引入avalon

    avalon是国内最强大的MVVM框架,没有之一,虽然淘宝KISSY团队也搞了两个MVVM框架,但都无疾而终.其他的MVVM框架都没几个.也只有外国人与像我这样闲的架构师才有时间钻研这东西.我很早之前 ...

  6. 迷你MVVM框架 avalonjs 学习教程20、路由系统

    SPA的成功离开不这三个东西,分层架构,路由系统,储存系统.分层架构是我们组织复杂代码的关键,这里特指MVVM的avalon:路由系统是将多个页面压缩在一个页面的关键:储存系统特指本地储存,是安全保存 ...

  7. 迷你MVVM框架 avalonjs 学习教程16、过滤器

    avalon的过滤器是参考自angular与rivets.它也被称做管道文本过滤器,它的处理对象只能是文本(字符串),它只能用在文本绑定中,并且只能是双花括号形式.下面是各大家的过滤器比较: rive ...

  8. 迷你MVVM框架 avalonjs 学习教程2、模块化、ViewModel、作用域

    一个项目是由许多人分工写的,因此必须要合理地拆散,于是有了模块化.体现在工作上,PM通常它这为某某版块,某某频道,某某页面.某一个模块,必须是包含其固有的数据,样式,HTML与处理逻辑.在jQuery ...

  9. 迷你MVVM框架 avalonjs 学习教程22、avalon性能大揭密

    avalon之所以能在页面处理1W个绑定(angular对应的数字是2000),出于两个重要设计--基于事件驱动的双向绑定链及智能CG回收机制. avalon的双向绑定链是通过Object.defin ...

随机推荐

  1. BaseAction编写:免去一些重复的代码,比如继承ActionSupport和实现ModelDriven接口

    1.BaseAction package com.learning.crm.base; import java.lang.reflect.ParameterizedType; import java. ...

  2. PHP $_SERVER 祥细解读(有事例)

    为了看的更明白,添加上了事例 例如  'www.ceshiyuming.com/ceshi.php?p=123';Array(    [HOSTNAME] =>     [PATH] => ...

  3. vue 感觉很好的渲染模式

    <ul v-if="todos.length"> <li v-for="todo in todos"> {{ todo }} </ ...

  4. BASIC-18_蓝桥杯_矩形面积交

    解题思路: 1.先将可能的情况列出,根据分类确定计算的方式; 示例代码: #include <stdio.h>#define N 8 int main(void){ int i = 0 , ...

  5. Python网络爬虫-xpath模块

    一.正解解析 单字符: . : 除换行以外所有字符 [] :[aoe] [a-w] 匹配集合中任意一个字符 \d :数字 [0-9] \D : 非数字 \w :数字.字母.下划线.中文 \W : 非\ ...

  6. bzoj 4842: [Neerc2016]Delight for a Cat

    Description ls是一个特别堕落的小朋友,对于n个连续的小时,他将要么睡觉要么打隔膜,一个小时内他不能既睡觉也打隔膜 ,因此一个小时内他只能选择睡觉或者打隔膜,当然他也必须选择睡觉或打隔膜, ...

  7. innodb的锁、update单条记录的花费时间压测

    观察innodb的锁时间,需要关注: mysqladmin extended-status -r -i 1 -uroot | grep "Innodb_row_lock_time" ...

  8. mongo获取lbs数据

    进入mongo目录执行./mongo 命令 #切换数据库use coachloc db.runCommand({geoNear : "coachloc" ,near : [113. ...

  9. centos7 设置系统时间与网络同步

    1.安装ntpdate工具 yum -y install ntp ntpdate 2.设置系统时间与网络时间同步 ntpdate cn.pool.ntp.org 3.将系统时间写入硬件时间 hwclo ...

  10. Windows下MySQL免安装版的安装、卸载

    一.安装 1.下载 到MySQL官网http://dev.mysql.com/downloads/mysql/ 下载mysql-5.6.15-win32.zip. 2.拷贝 将mysql-5.6.15 ...