探索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 相关文章推荐
jquery随机展示头像代码
Dec 21 Javascript
Javascript 面向对象(二)封装代码
May 23 Javascript
纯JS实现五子棋游戏兼容各浏览器(附源码)
Apr 24 Javascript
javascript陷阱 一不小心你就中招了(字符运算)
Nov 10 Javascript
JavaScript lastIndexOf方法入门实例(计算指定字符在字符串中最后一次出现的位置)
Oct 17 Javascript
9款2014最热门jQuery实用特效推荐
Dec 07 Javascript
angular.foreach 循环方法使用指南
Jan 06 Javascript
js实现鼠标滑过文字链接色彩变化的效果
May 06 Javascript
javascript创建cookie、读取cookie
Mar 31 Javascript
基于canvas实现手写签名(vue)
May 21 Javascript
vue实现井字棋游戏
Sep 29 Javascript
node.js如何根据URL返回指定的图片详解
Oct 21 Javascript
关于使用 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
jQuery Ajax异步处理Json数据详解
2013/11/05 Javascript
JS实现部分HTML固定页面顶部随屏滚动效果
2015/12/24 Javascript
js实现字符串和数组之间相互转换操作
2016/01/12 Javascript
基于JS代码实现图片在页面中旋转效果
2016/06/16 Javascript
基于jquery实现弹幕效果
2016/09/29 Javascript
Bootstrap笔记—折叠实例代码
2017/03/13 Javascript
基于BootStrap multiselect.js实现的下拉框联动效果
2017/07/28 Javascript
JS中call和apply函数用法实例分析
2018/06/20 Javascript
Vue头像处理方案小结
2018/07/26 Javascript
es6 for循环中let和var区别详解
2020/01/12 Javascript
用Python实现通过哈希算法检测图片重复的教程
2015/04/02 Python
Python中用sleep()方法操作时间的教程
2015/05/22 Python
python使用win32com库播放mp3文件的方法
2015/05/30 Python
python实现的系统实用log类实例
2015/06/30 Python
Python语言实现获取主机名根据端口杀死进程
2016/03/31 Python
【Python】Python的urllib模块、urllib2模块批量进行网页下载文件
2016/11/19 Python
Python实现定时任务
2017/02/08 Python
一个基于flask的web应用诞生 用户注册功能开发(5)
2017/04/11 Python
python spyder中读取txt为图片的方法
2018/04/27 Python
python3实现windows下同名进程监控
2018/06/21 Python
python中的不可变数据类型与可变数据类型详解
2018/09/16 Python
python3.6.3安装图文教程 TensorFlow安装配置方法
2020/06/24 Python
关于sys.stdout和print的区别详解
2019/12/05 Python
Python3爬虫关于识别点触点选验证码的实例讲解
2020/07/30 Python
Jupyter安装拓展nbextensions及解决官网下载慢的问题
2021/03/03 Python
CSS3美化表单控件全集
2016/06/29 HTML / CSS
广州盈通面试题
2015/12/05 面试题
三年级音乐教学反思
2014/01/28 职场文书
庆祝教师节活动方案
2014/01/31 职场文书
三八妇女节活动主持词
2014/03/17 职场文书
暑期社会实践先进个人主要事迹
2014/05/22 职场文书
群众路线自我剖析范文
2014/11/04 职场文书
英语专业毕业论文答辩开场白
2015/05/27 职场文书
小学语文新课改心得体会
2016/01/22 职场文书
原来闭幕词是这样写的呀!
2019/07/01 职场文书
JavaScript canvas实现流星特效
2021/05/20 Javascript