探索Emberjs制作一个简单的Todo应用


Posted in Javascript onNovember 07, 2012

目标
使用Emberjs制作一个简单的Todo应用,实现这样一个效果:通过在文本框输入文本,创建一条代办事项,代办事项可以选择优先级,完成的事项可以删除。

准备
完成这个应用,需要做点准备:
1、创建一个html页面,暂时不管样式;
2、脚本:emberjs,handlebars、jQuery。这三个脚本可以从网上获得,我们将把他们加入到head标签里去。

制作
创建页面,加入脚本,就可以开始制作应用。html代码如下:

<!doctype html> 
<html> 
<head> 
<meta charset="utf-8"> 
<title>Ember--第一个应用</title> 
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script> 
<script type="text/javascript" src="http://cloud.github.com/downloads/wycats/handlebars.js/handlebars-1.0.rc.1.js"></script> 
<script type="text/javascript" src="http://cloud.github.com/downloads/emberjs/ember.js/ember-1.0.0-pre.2.min.js"></script> 
</head> 
<body> 
</body> 
</html>

按照ember的要求,需要用Ember.Application.create()先创建应用实例,这也作为应用的命名空间。这个create方法可以传递一个对象属性ready,属性值是一个函数,在应用准备就绪时调用。Ember还可以使用缩写Em来代替。

在Ember中有一个Em.Logger对象,相当于window.console,可以用来调试。我们可以在这个ready加入一个消息,显示在控制台中。

现在,在head标签里再增加一个script标签来写应用的脚本,实例化一个ember应用,顺便把MVC各模块的区域也加上。脚本代码如下:

/******************** 
application 
********************/ window.App = Ember.Application.create( 
{ 
ready:function(){ 
Em.Logger.info('欢迎使用待办事项应用'); 
} 
} 
); 
/******************** 
model 
********************/ 

/******************** 
view 
********************/ 

/******************** 
controlle 
********************/

然后,我们需要一个输入框来输入代办事项,需要创建一个ember文本框视图。ember视图可以用Ember.View类来创建(使用create方法)或扩展(使用extend方法)一个新的视图类。不过对于文本框视图,ember提供了更直接的方式——Ember.TextField类,我们可以先使用这个类来扩展一个自定义的视图,然后再实例化添加到页面上。我们将这个文本框视图类命名AddItemView 。

在脚本代码里的view区域添加上文本框视图代码:

App.AddItemView = Ember.TextField.extend({ });

可以给它加个提示语,html5支持placeholder,可以拿来用。还需要在按下回车时将内容添加到代表事项列表,这里需要用到一个属性:insertNewline,在按下回车时会调用相应的函数。加入后的代码如下:

App.AddItemView = Ember.TextField.extend({ 
placeholder:'输入待办事项', 
insertNewline:function(){} 
});

由于现在还没确定具体添加方法,函数体暂时先不写。

用户在按下回车时增加一条代办事项,需要一个列表来显示,在ember中可以创建CollectionView来存放列表项目视图,对于CollectionView,默认会有一个content属性用于存放列表项目对象,其属性值是一个数组。为了让其列表显示为ul列表,需要定义CollectionView的标签名(tagName)为“ul”。我们给这个列表视图命名为ListView,并增加到文本框视图的下方。最后代码如下:

App.ListView = Ember.CollectionView.extend({ 
content:[], 
tagName:'ul' 
});

现在如果打开页面,是没显示任何内容的,因为视图还没被渲染,要将视图显示出来,需要handlebar模板的支持。

现在来修改html页面的body块,加入刚创建的两个视图,看看效果。

添加handlebar模板的方法是<script type="text/x-handlebars">/*视图助手*/</script>,还可以指定模板名称,在data-template-name属性里定义,待会我们添加列表项目时会需要用到。

在模板里需要通过视图助手(helper)来添加视图,语法也很简单,用两个花括号对包裹,里面通过模板关键字来指定要显示的视图,如:{{view App.AddItemView}}。其他模板助手可以在handlebar网站查到:http://handlebarsjs.com/。

现在先把文本框跟列表视图添加到页面上,修改后的body代码如下:

<body> 
<script type="text/x-handlebars"> 
<span>请输入待办事项:</span>{{view App.AddItemView}}<br/> {{view App.ListView}} 
</script> 
</body>

现在刷新页面,会显示一句“请输入代办事项”跟一个文本框,列表由于没有内容,不会显示。

这个时候我们可以在content里添加点内容,比如content:['a','b','c'],然后刷新页面,你会发现列表区域只有三个小黑点(如果你没重置列表样式的话)。因为你在content里添加了三项,但列表项还没有指定一个显示的模板,所以,显示为空。为了让你看到效果,我们来给列表项定义个显示的模板吧。这里需要处理两个地方,第一是在页面加指定名称的模板,第二是在列表视图里定义列表项目的属性。

定义列表项目,需要用到itemViewClass,它会将每个content项传递进去并用指定的模板显示。先来修改列表视图ListView 吧,给它增加itemViewClass属性,这也是一种视图,所以需要用Ember.View来创建,在创建时同时指定用来显示的模板名称为itemTemplate,这个名称同时将为出现在html的handlebar模板名称里。修改后的代码如下:

App.ListView = Ember.CollectionView.extend({ 
content:['a','b','c'], 
tagName:'ul', 
itemViewClass: Ember.View.extend({ 
templateName:'itemTemplate', 
}) 
});

还差一步就完成了,现在来修改html,我们需要在body里再新建一个handlebar模板,并且会用到上面给出的模板名称,代码如下:

<script type="text/x-handlebars" data-template-name="itemTemplate"> </script>

接着同样是添加模板助手,要把每一个content项传递给助手,会用到view.content。添加如下代码:

<script type="text/x-handlebars" data-template-name="itemTemplate"> 
{{view.content}} 
</script>

完成后,刷新页面,现在终于把content里的内容显示出来了,而且,模板会自动加上li标签。

继续完善我们的应用。我们总不能把content的内容写成固定的吧,这样用户还怎么添加呢。所以,现在考虑把用户要添加的项目保存到一个数组里,然后content自己去取这个数组的内容。同时,ember框架支持双向绑定,当数组内容修改时,通过绑定的content也会同时改变,反之亦然。现在,就创建一个ember数组,然后跟content绑定吧。

ember数组可以通过ArrayController类来创建,它会把你传进去的普通javascript转变为一个新的ember数组对象,我们把用来管理项目的数组命名为todoStore,放到html页面的controller区域,创建的代码如下:

App.todoStore = Ember.ArrayController.create({ 
content:[] 
});

现在可以把ListView 里的content数组放到这个todoStore 的数组里,然后绑定ListView 里的content到todoStore 上,这两个对象将修改为如下所示:

App.ListView = Ember.CollectionView.extend({ 
contentBinding:'App.todoStore', 
tagName:'ul', 
itemViewClass: Ember.View.extend({ 
templateName:'itemTemplate' 
}) 
}); /******************** 
controlle 
********************/ 
App.todoStore = Ember.ArrayController.create({ 
content:['a','b','c'] 
});

Binding是个后缀,表示绑定,属性值是绑定的对象,默认取该对象的content属性。修改完成后刷新页面,如果你看到的页面跟修改之前的一样,说明修改成功了。接着,是时候去掉content里的值了,我们需要的数据将由用户在文本框里输入。

考虑现在的交互过程,用户在文本框输入内容,按下回车,程序获取到该事件,调用一个方法创建一个新对象,再把这个新对象送给todoStore ,由于绑定作用,列表会自动增加一项。

是时候改造下文本框视图了,还记得insertNewline吗?我们可以在这里创建新的项目。我们会用到三个方法:set()设置属性值、get()获取属性值、pushObject()添加数据,修改AddItemView 后的代码如下:

App.AddItemView = Ember.TextField.extend({ 
placeholder:'输入待办事项', 
insertNewline:function(){ 
var item = this.get('value'); 
App.todoStore.pushObject(item); 
this.set('value',''); 
} 
});

现在刷新页面,然后输入内容,按回车,列表会添加输入的内容,说明修改成功,如果你没把todoStore 的content属性里的内容清空的话,现在会有4个列表项了。

距离我们的目标还有一半啊,我们还缺少两个功能:选择优先级跟删除完成的项目。

要增加下拉列表,可以使用另一个方便的视图:Ember.Select。我们可以直接在模板里直接创建一个,同样通过绑定,把下拉列表视图的content绑定到另一个ember对象上,然后设置默认选中的优先级。优先级也需要用到绑定,这样在页面上选择的时候,才会同时修改对应的ember对象里的内容。先来创建这个ember对象,自定义该对象的selected属性表示选中的值,其他名称也行,这段代码会加到todoStore对象的下面,命名为selectController,代码如下:

App.selectController = Ember.Object.create({ 
selected:'低', 
content:['高','中','低'] 
});

然后增加一个模板助手,并绑定selectController 里对应的属性,选中项的绑定需要用到selectionBinding,顺便给个文字提示,然后添加到文本框模板的下面,修改后的代码如下:

<script type="text/x-handlebars"> 
<span>请输入待办事项:</span>{{view App.AddItemView}}<br/> 
<span>请选择优先级:</span>{{view Ember.Select contentBinding="App.selectController.content" selectionBinding="App.selectController.selected"}} 
{{view App.ListView}} 
</script>

现在刷新页面就会出现下拉列表了。

要想让列表项也出现这个优先级,还得花点功夫啊。是时候用model了,我们来创建一个model类,当按下回车时,从这个类创建一个实例,再把实例扔到todoStore里就可以了,另外,模板也要跟着修改,文本框视图的创建方法也得改。这次改动比较多了点。另外,还会用到一个计算属性的功能,当依赖的属性变化时,自动更新。把这个model命名为TodoModel,放到model区域,创建代码如下:

/******************** 
model 
********************/ App.TodoModel = Em.Object.extend({ 
status:'', 
value:'', 
title:function(){ 
return '['+this.get('status')+']'+this.get('value'); 
}.property('status','value') 
});

status表示选择的优先级,value表示文本框里的值,title表示列表项目要显示的内容,这些属性名都是自定义的。其中title不需要提供,因为它设置为计算属性,依赖于status跟value属性,自动计算获取,property()方法就是ember函数转变为计算属性的方法,后面的参数表示title依赖的属性,当status或value变化时,就会自动给出。

接着修改AddItemView的insertNewline属性,需要取到两个数据,一个是文本框里的内容,一个是下拉列表里选中的项目。文本框的值已经知道怎么获取了,下拉列表的值呢?别忘了我们已经将选中项绑定到selectController里的selected属性了,直接从那里取就可以了。修改后的AddItemView代码如下:

App.AddItemView = Ember.TextField.extend({ 
placeholder:'输入待办事项', 
elementId:'add', 
insertNewline:function(){ 
var item = App.TodoModel.create({ 
status:App.selectController.get('selected'), 
value:this.get('value') 
}); 
App.todoStore.pushObject(item); 
this.set('value',''); 
} 
});

现在可以通过TodoModel类来实例化一个待办项目并添加到todoStore里了,最后是修改项目列表的模板itemTemplate来显示,在模板里需要取到当前项目的title值来显示,代码如下:

<script type="text/x-handlebars" data-template-name="itemTemplate"> 
{{view.content.title}} 
</script>

现在添加的新待办事项会显示优先级了。

好了,最后一个功能,删除。跟添加pushObject相反,删除用到的是removeObject。因为它是显示在每个列表项目里的,所以,需要修改itemViewClass跟itemTemplate模板,我们在itemViewClass里添加一个方法,当用户点击时调用,把该方法命名为removeItem,代码如下:

App.ListView = Ember.CollectionView.extend({ 
contentBinding:'App.todoStore', 
tagName:'ul', 
itemViewClass: Ember.View.extend({ 
templateName:'itemTemplate', 
removeItem:function(){this.getPath( 'contentView.content' ).removeObject(this.get( 'content' ));} 
}) 
});

最后是在itemTemplate模板里增加一个接受点击的链接,用到的是action助手,第一个参数是方法名,target属性用来指定对象,点击时调用指定对象下的方法。修改后的itemTemplate代码如下:

<script type="text/x-handlebars" data-template-name="itemTemplate"> 
{{view.content.title}} <a href="#" {{action removeItem target="this"}} >X</a> 
</script>

现在新增加的项目都会有个叉在右边,点击时就把当前项目删除。

最后还可以做点改进,当鼠标移动到项目上时才显示删除链接,完成这个功能,需要修改itemViewClass以及在模板里增加逻辑判断助手{{#if}}。你可以自己试着去做,也可以看看最后完整的代码。

full code 
<!doctype html> 
<html> 
<head> 
<meta charset="utf-8"> 
<title>Ember--第一个应用</title> 
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script> 
<script type="text/javascript" src="http://cloud.github.com/downloads/wycats/handlebars.js/handlebars-1.0.rc.1.js"></script> 
<script type="text/javascript" src="http://cloud.github.com/downloads/emberjs/ember.js/ember-1.0.0-pre.2.min.js"></script> 
<script> /******************** 
application 
********************/ 
window.App = Ember.Application.create( 
{ 
ready:function(){ 
Em.Logger.info('欢迎使用待办事项应用'); 
} 
} 
); 
/******************** 
model 
********************/ 
App.TodoModel = Em.Object.extend({ 
status:'', 
value:'', 
title:function(){ 
return '['+this.get('status')+']'+this.get('value'); 
}.property('status','value') 
}); 
/******************** 
view 
********************/ 
App.AddItemView = Ember.TextField.extend({ 
placeholder:'输入待办事项', 
elementId:'add', 
insertNewline:function(){ 
var item = App.TodoModel.create({ 
status:App.selectController.get('selected'), 
value:this.get('value') 
}); 
App.todoStore.pushObject(item); 
this.set('value',''); 
} 
}); 
App.ListView = Ember.CollectionView.extend({ 
contentBinding:'App.todoStore', 
tagName:'ul', 
itemViewClass: Ember.View.extend({ 
templateName:'itemTemplate', 
removeItem:function(){this.getPath( 'contentView.content' ).removeObject(this.get( 'content' ));}, 
mouseEnter:function(){this.set('hover',true);}, 
mouseLeave:function(){this.set('hover',false);} 
}) 
}); 

/******************** 
controlle 
********************/ 
App.todoStore = Ember.ArrayController.create({ 
content:[] 
}); 
App.selectController = Ember.Object.create({ 
selected:'低', 
content:['高','中','低'] 
}); 
</script> 
</head> 
<body> 
<script type="text/x-handlebars"> 
<span>请输入待办事项:</span>{{view App.AddItemView}}<br/> 
<span>请选择优先级:</span>{{view Ember.Select contentBinding="App.selectController.content" 
selectionBinding="App.selectController.selected"}} 
{{view App.ListView}} 
</script> 
<script type="text/x-handlebars" data-template-name="itemTemplate"> 
{{view.content.title}} {{#if view.hover}}<a href="#" {{action removeItem target="this"}} >X</a>{{/if}} 
</script> 
</body> 
</html>
Javascript 相关文章推荐
IE8提示Invalid procedure call or argument 异常的解决方法
Sep 30 Javascript
解析javascript系统错误:-1072896658的解决办法
Jul 08 Javascript
jquery提交form表单简单示例分享
Mar 03 Javascript
jQuery插件开发汇总
May 15 Javascript
Angular使用ng-messages与PHP进行表单数据验证
Dec 28 Javascript
JS动态生成年份和月份实例代码
Feb 04 Javascript
vue2.0中vue-cli实现全选、单选计算总价格的实例代码
Jul 18 Javascript
JavaSctit 利用FileReader和滤镜上传图片预览功能
Sep 05 Javascript
如何以Angular的姿势打开Font-Awesome详解
Apr 22 Javascript
layui前端框架之table表数据的刷新方法
Aug 17 Javascript
jqGrid表格底部汇总、合计行footerrow处理
Aug 21 Javascript
jQuery实现简单QQ聊天框
Aug 27 jQuery
关于使用 jBox 对话框的提交不能弹出问题解决方法
Nov 07 #Javascript
seajs1.3.0源码解析之module依赖有序加载
Nov 07 #Javascript
Javascript引用指针使用介绍
Nov 07 #Javascript
JavaScript在多浏览器下for循环的使用方法
Nov 07 #Javascript
Javascript的数组与字典用法与遍历对象的属性技巧
Nov 07 #Javascript
JS正则中的RegExp对象对象
Nov 07 #Javascript
js模拟点击事件实现代码
Nov 06 #Javascript
You might like
php中全局变量global的使用演示代码
2011/05/18 PHP
解析:php调用MsSQL存储过程使用内置RETVAL获取过程中的return值
2013/07/03 PHP
作为PHP程序员你要知道的另外一种日志
2018/07/30 PHP
CodeIgniter框架钩子机制实现方法【hooks类】
2018/08/21 PHP
ext实现完整的登录代码
2008/08/08 Javascript
javascript中利用数组实现的循环队列代码
2010/01/24 Javascript
等待指定时间后自动跳转或关闭当前页面的js代码
2013/07/09 Javascript
让js弹出窗口居前显示的实现方法
2013/07/10 Javascript
JavaScript中window、doucment、body的解释
2013/08/14 Javascript
JQuery对class属性的操作实现按钮开关效果
2013/10/11 Javascript
javascript内置对象arguments详解
2014/03/16 Javascript
jquery密码强度校验
2015/12/02 Javascript
jQuery实现鼠标跟随提示层效果代码(可显示文本,Div,Table,Html等)
2016/04/18 Javascript
解析AngularJS中get请求URL出现的跨域问题
2016/12/01 Javascript
jQuery验证表单格式的使用方法
2017/01/10 Javascript
javascript变量提升和闭包理解
2018/03/12 Javascript
JavaScript使用递归和循环实现阶乘的实例代码
2018/08/28 Javascript
vue component 中引入less文件报错 Module build failed
2019/04/17 Javascript
js中let能否完全替代IIFE
2019/06/15 Javascript
微信小程序实现点击按钮后修改颜色
2019/12/05 Javascript
Vue中computed及watch区别实例解析
2020/08/01 Javascript
pymongo实现多结果进行多列排序的方法
2015/05/16 Python
简单实现python收发邮件功能
2018/01/05 Python
Django 中使用流响应处理视频的方法
2018/07/20 Python
详解Python中的正斜杠与反斜杠
2019/08/09 Python
python 实现批量替换文本中的某部分内容
2019/12/13 Python
Python中zip函数如何使用
2020/06/04 Python
python爬虫---requests库的用法详解
2020/09/28 Python
使用AJAX和Django获取数据的方法实例
2020/10/25 Python
HTML5声音录制/播放功能的实现代码
2018/05/03 HTML / CSS
银行员工职业规划范文
2014/01/21 职场文书
《雨点》教学反思
2014/02/12 职场文书
个人批评与自我批评发言稿
2014/09/28 职场文书
2016年小学端午节活动总结
2016/04/01 职场文书
小学语文教师竞聘演讲稿范文
2019/08/09 职场文书
Python+OpenCV实现图片中的圆形检测
2022/04/07 Python