我们将创建Cookbook应用,提供菜谱的创建和删除功能,他看起来像下面的样子。
javascriptMVC使用 generator scripts 生成工具帮助你创建你的文件夹和文件,让你的工作变得更加轻松。
创建应用程序
创建应用程序,首先打开win的命令窗口,导航到应用程序的根目录,运行下面的代码:
> js jquery\generate\app cookbook
脚本会帮你创建应用程序的文件夹和文件,目录如下:
cookbook/ // 应用程序目录
cookbook.css // app 的 css
cookbook.html // app 首页
cookbook.js // app js
docs/ // 文档
fixtures/ // 模拟ajax请求
funcunit.html // functional 测试页面
models/ // 模型数据层
qunit.html // unit 测试页面
scripts/ // 命令行脚本
build.html // build 脚本页面
build.js // build 脚本
clean.js // clean代码
crawl.js // 生成 search 内容
docs.js // 创建文档
test/
funcunit // functional 测试
cookbook_test.js // functional 测试
funcunit.js // 加载functional 测试
qunit/ // unit 测试
cookbook_test.js // unit 测试
qunit.js // 加载unit 测试
下面是我在自己机器上运行的效果:
生成的cookbook文件夹:
cookbook文件夹中的文件:
我们将使用cookbook.html作为应用首页,如果你需要生成别的页面,可以使用下面的命令:
> js jquery\generate\page cookbook index.html
在页面中添加下面的scripts标签:
<script type='text/javascript' src='../path/to/steal/steal.js?cookbook'></script>
打开cookbook/cookbook.html页面,会显示下面的结果。
打开cookbook/cookbook.html页面,你会发现
<script type='text/javascript' src='../steal/steal.js?cookbook'>
他会加载steal,steal会加载cookbook/cookbook.js,打开cookbook.js文件,你会看到下面的内容。
steal( './cookbook.css', // app CSS 文件 './models/models.js', // steals 所有 models './fixtures/fixtures.js', // 创建 models 的模拟ajax请求 function(){ // 配置 app })
应用程序文件加载和配置应用程序资源,包括css、js等。接下来我们创建插件工具、model、模拟请求等,并允许我们执行创建和删除菜单的操作。
搭建菜谱
我们将使用scaffold generator去快速创建菜谱,他会实现下列功能:
- 菜谱 model 实现服务器端的CRUD操作
- 模拟菜谱的服务器端请求
- 创建菜谱的插件
- 罗列和删除菜谱的插件
在命令下执行下面的命令,完成上述工作。
> js jquery\generate\scaffold Cookbook.Models.Recipe
下面介绍生成的可部分代码及功能:
recipe.js:recipe模型,用于实现服务器端对recipe执行创建、获取、更新和删除操作。
recipe_test.js:测试recipe模型。
fixtures.js:模拟recipe的ajax请求。
recipe/create:文件夹下面包含创建recipe插件的代码、demo页面和测试代码。
recipe/list:文件夹下面包含罗列recipe插件的代码、demo页面和测试代码。
(steal added)
generator同时会罗列那些显示(steal added)文件,例如:
cookbook/models/models.js (steal added)
截图:
"(steal added)"的意思是你的文件需要加载别的文件,generator在你的文件中使用steal为你完成了这个工作。例如,cookbook/models/models.js需要加载 recipe.js,我们来看models.js的代码:
// 加载 model 文件 steal("jquery/model", './recipe.js')
设置页面
generator运行之后,cookbook.js文件的代码如下:
steal( './cookbook.css', // app CSS 文件 './models/models.js', // 加载所有 models './fixtures/fixtures.js', // 为 models 创建模拟请求 'cookbook/create', 'cookbook/list', function(){ // 配置你的应用 $('#recipes').cookbook_recipe_list(); $('#create').cookbook_recipe_create(); })
你会注意到在完成cookbook/create
和cookbook/list加载中后,他们会把插件添加到
#recipes
和 #create元素上。但是这两个元素并不存在,打开cookbook/cookbook.html文件,添加下面的代码完成对两元素的添加:
<!DOCTYPE HTML> <html lang="en"> <head> <title>cookbook</title> </head> <body> <h1>Welcome to JavaScriptMVC 3.2!</h1> <ul id='recipes'></ul> <form id='create' action=''></form> <script type='text/javascript' src='../steal/steal.js?cookbook'> </script> </body> </html>
运行程序
现在我们可以运行程序了,在浏览器打开cookbook/cookbook.html,我们来看运行结果。
他是如何工作的
cookbook应用可以分割为5个部分:
- Recipe 模型(Model)
- Recipe 请求模拟(Fixture)
- Recipe 创建控制
- Recipe 显示控制
- Cookbook 应用程序,组合所有组件共同工作
Recipe模型
cookbook/models/recipe.js看起来像这样:
steal('jquery/model', function(){ $.Model('Cookbook.Models.Recipe', { findAll: "/recipes.json", findOne : "/recipes/{id}.json", create : "/recipes.json", update : "/recipes/{id}.json", destroy : "/recipes/{id}.json" }, {}); })
所有加载$.Model然后用它创建了Cookbook.Models.Recipe类,该类为我们提供了创建、获取、更新和销毁的功能,我们来看例子。
create
// 创建一个 recipe 实例 var recipe = new Cookbook.Models.Recipe({ name: 'Hot Dog', description: 'nuke dog, put in bun' }) // 调用 save 在服务器端保存实例 recipe.save()
retrieve
// 从服务器获取 recipes Cookbook.Models.Recipe.findAll({}, function(recipes){ // 使用recipes })
update
// 更新 recipe 属性 recipe.attrs({ name: 'Bratwurst', description: 'nuke bratwurst, put in bun' }); // 调用 save 保存数据到服务器 recipe.save()
delete
// 调用 destroy recipe.destroy()
我们没有服务器端,所以我们要使用fixture来模拟。
Recipe Fixture
Fixtures可以截获ajax请求并模拟返回结果,使用它的好处就是你可以在没有服务器的情况下开发你的前台代码。打开 cookbook/fixtures/fixtures.js你会发现下面的代码:
$.fixture.make("recipe", 5, function(i, recipe){ var descriptions = ["grill fish", "make ice", "cut onions"] return { name: "recipe "+i, description: $.fixture.rand( descriptions , 1) } })
他为我们模拟了5个recipe。
Recipe创建控制
在浏览器打开cookbook/recipe/create/create.html,这里展示了Cookbook.Recipe.Create control控件,他为我们提供创建recipe的功能。我们打开cookbook/recipe/create/create.js来看她时如何实现的:
steal( 'jquery/controller', 'jquery/view/ejs', 'jquery/dom/form_params', 'jquery/controller/view', 'cookbook/models' ) .then('./views/init.ejs', function($){ $.Controller('Cookbook.Recipe.Create', { init : function(){ this.element.html(this.view()); }, submit : function(el, ev){ ev.preventDefault(); this.element.find('[type=submit]').val('Creating...'); new Cookbook.Models.Recipe(el.formParams()).save(this.callback('saved')); }, saved : function(){ this.element.find('[type=submit]').val('Create'); this.element[0].reset(); } }); });
代码使用steal来加载依赖项,然后创建Cookbook.Recipe.Create控制器,他创建了名为cookbookrecipecreate的jquery helper,我们可以这样使用它:
$('form#create').cookbook_recipe_create()
当jquery插件被调用的时候,init方法会被执行。
this.element.html(this.view());
上面的代码把模版cookbook/recipe/create/views/init.ejs,加载到控制器所在的html元素上。当jquery插件被调用的时候,相关事件也会绑定到控制器的元素上,例如上面的代码就实现了对submit事件的监听。当有submit事件发生的时候,submit按钮的值会被修改,同时会创建一个新的recipe对象。
Recipe显示控制
在浏览器打开cookbook/recipe/create/create.html页面,这里展示了Cookbook.Recipe.List控件。他从服务器端加载recipe,并允许你执行删除操作。同时他也提供了创建recipe的功能,并会把记录展示。打开cookbook/recipe/list/list.js,我们来看Cookbook.Recipe.List的代码:
$.Controller('Cookbook.Recipe.List', { init : function(){ this.element.html(this.view('init',Cookbook.Models.Recipe.findAll()) ) }, '.destroy click': function( el ){ if(confirm("Are you sure you want to destroy?")){ el.closest('.recipe').model().destroy(); } }, "{Cookbook.Models.Recipe} destroyed" : function(Recipe, ev, recipe) { recipe.elements(this.element).remove(); }, "{Cookbook.Models.Recipe} created" : function(Recipe, ev, recipe){ this.element.append(this.view('init', [recipe])) }, "{Cookbook.Models.Recipe} updated" : function(Recipe, ev, recipe){ recipe.elements(this.element) .html(this.view('recipe', recipe) ); } });
当List加载到页面,init方法会被执行。
this.element.html(this.view('init',Cookbook.Models.Recipe.findAll()) )
上面一行代码完成了下面4项工作:
- 从服务器端请求recipes
- 加载
cookbook/recipe/list/views/init.ejs
模版 - 当 recipes 数据返回,并且模版完成加载,呈现结果
- 把结果插入到队列中
init.ejs文件内容:
<%for(var i = 0; i < this.length ; i++){ %> <li <%= this[i]%> > <%== $.View('//cookbook/recipe/list/views/recipe.ejs', this[i] )%> </li> <%}%>
上面的代码会遍历从服务器端获取的结果,为每个recipe创建li对象,并且会渲染子模版。注意到代码在<li>内部添加了
<%= this[i]%>
他会把model添加到jQuery.data中,并会为li添加一个‘recipe’ 的 className,我们之后会使用到。
销毁Recipe
每一个recipe都有一个销毁的链接,点击它之后会执行'.destroy click'方法:
if(confirm("Are you sure you want to destroy?")){ el.closest('.recipe').model().destroy(); }
他会首先询问你时候真要执行销毁操作,如果真要销毁,他通过recipe的父元素获取model实例(在jquery.data中),然后执行销毁操作。当实例被销毁的时候,destroyed事件会被执行,他是通过下面的方式注册的:
"{Cookbook.Models.Recipe} destroyed" : function(Recipe, ev, recipe) { recipe.elements(this.element).remove(); }
当销毁事件发生的时候,List控制器会查询[jQuery.fn.elements elements]获取到jQuery.data中的实例,然后销毁它。
创建Recipe
当recipe创建的时候,List监听的"created"事件会被触发。
"{Cookbook.Models.Recipe} created" : function(Recipe, ev, recipe){ this.element.append(this.view('init', [recipe])) }
所以当recipe被创建的时候,init视图会被渲染并把结果加载到recipe元素中。
更新Recipe
当recipe更新的时候,List监听的"updated"事件会被触发。
"{Cookbook.Models.Recipe} updated" : function(Recipe, ev, recipe){ recipe.elements(this.element) .html(this.view('recipe', recipe) ); }
当recipe更新的时候,List会更新html页面。
整合代码
cookbook应用加载所有相关组件,并把他们加载到页面。当Cookbook.Recipe.Create创建一个recipe,他会创建一个Cookbook.Recipe.List监听的'created'事件,然后把新创建的对象添加在列表中。
最后我把demo附上,还是老样子,demo需要在iis等服务器上执行,javascriptMVC的代码自己去下。
javascriptMVC下载地址:https://github.com/downloads/jupiterjs/javascriptmvc/javascriptmvc-3.2.4.zip
demo下载地址:javascriptmvc_cookbook.zip