探索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 相关文章推荐
两个listbox实现选项的添加删除和搜索
Mar 01 Javascript
caller和callee的区别介绍及演示结果
Mar 10 Javascript
jquery实现加载进度条提示效果
Nov 23 Javascript
Bootstrap每天必学之导航
Nov 26 Javascript
JavaScript事件学习小结(五)js中事件类型之鼠标事件
Jun 09 Javascript
分享JavaScript监听全部Ajax请求事件的方法
Aug 28 Javascript
如何使用Bootstrap创建表单
Mar 29 Javascript
基于JavaScript实现弹幕特效
Aug 27 Javascript
详解如何使用PM2将Node.js的集群变得更加容易
Nov 15 Javascript
在vue-cli中组件通信的方法
Dec 16 Javascript
Vue.js实现的计算器功能完整示例
Jul 11 Javascript
浅谈node.js中间件有哪些类型
Apr 29 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
php基础知识:控制结构
2006/12/13 PHP
那些年一起学习的PHP(一)
2012/03/21 PHP
php生成局部唯一识别码LUID的代码
2012/10/06 PHP
浅谈php命令行用法
2015/02/04 PHP
thinkphp5.1框架中容器(Container)和门面(Facade)的实现方法分析
2019/08/05 PHP
跨浏览器的 mouseenter mouseleave 以及 compareDocumentPosition的使用说明
2010/05/04 Javascript
JS localStorage实现本地缓存的方法
2013/06/22 Javascript
jQuery实现单击和鼠标感应事件
2015/02/01 Javascript
JS中检测数据类型的几种方式及优缺点小结
2016/12/12 Javascript
javaScript生成支持中文带logo的二维码(jquery.qrcode.js)
2017/01/03 Javascript
Ajax异步文件上传与NodeJS express服务端处理
2017/04/01 NodeJs
微信小程序的分类页面制作
2017/06/27 Javascript
webpack+vue+express(hot)热启动调试简单配置方法
2018/09/19 Javascript
详解a标签添加onclick事件的几种方式
2019/03/29 Javascript
Vue使用axios出现options请求方法
2019/05/30 Javascript
微信小程序开发注意指南和优化实践(小结)
2019/06/21 Javascript
JavaScript实现轮播图效果
2020/10/30 Javascript
vue打开其他项目页面并传入数据详解
2020/11/25 Vue.js
python实现简单温度转换的方法
2015/03/13 Python
Django admin美化插件suit使用示例
2017/12/12 Python
对python list 遍历删除的正确方法详解
2018/06/29 Python
浅析python的优势和不足之处
2018/11/20 Python
Python后台开发Django的教程详解(启动)
2019/04/08 Python
Python PyCharm如何进行断点调试
2019/07/05 Python
python sklearn包——混淆矩阵、分类报告等自动生成方式
2020/02/28 Python
微软澳洲官方网站:Microsoft Australia
2017/01/10 全球购物
女子锻炼服装和瑜伽服装:Splits59
2019/03/04 全球购物
PHP引擎php.ini参数优化深入讲解
2021/03/24 PHP
营业员演讲稿
2013/12/30 职场文书
大学生学业生涯规划
2014/01/05 职场文书
2014年行政助理工作总结
2014/11/19 职场文书
骨干教师事迹材料
2014/12/17 职场文书
酒店前台接待岗位职责
2015/04/02 职场文书
100句人生哲理语录集锦:强者征服今天,懒汉坐等明天
2019/10/18 职场文书
Python实战之用tkinter库做一个鼠标模拟点击器
2021/04/27 Python
mysql数据库入门第一步之创建表
2021/05/14 MySQL