如何使用JavaScriptMVC开发项目第一步,就是创建应用程序。
在这里我们使用一个例子开始讲解。
下面我们将创建一个基本厨师手册应用系统,它让我们可以创建和删除食谱。如下图:
JavaScriptMVC提供生成脚本来帮助你们安装应用程序的文件和文件夹。
1、创建一个应用程序
在创建你的应用程序之前,打开一个Window系统的控制台窗口,即doc命令窗口,并且把目录定位到JavaScriptMVC包目录下。
在DOC命令窗口,执行:
js jquery/generate/app cookbook
这个脚本将生成一个应用程序的文件夹和文件。生成最张的程序目录如下:
cookbook/ // 应用程序的主目录文件夹
cookbook.css // 默认的应用程序样式文件
cookbook.html // 默认的应用程序主页面文件
cookbook.js // 应用程序主文件,相当我们main方法,加载所有程序需要的资源
docs/ // 帮助文件文件夹
fixtures/ // 模拟的Ajax请求服务端
funcunit.html // 功能的测试页面,主要测试C和V
models/ // 模型 即MVC中的M数据层
qunit.html // 单元测试页面,主要测试M
scripts/ // 命令行脚本
build.html // html for build script
build.js // 压缩程序所有文件,这个命令可以让我们只压缩项目中需要的资源
clean.js // code cleaning / linting
crawl.js // generate search content
docs.js // 生成帮助文档,相对我们之前的Web项目,这个可以让Web阅读和维护起到很大的帮助
test/
funcunit // 功能测试文件夹,包含程序中所有功能的测试文件
cookbook_test.js // cookbook功能的测试文件
funcunit.js // 加载程序中所有功能的测试文件
qunit/ // 单元测试文件夹,包含程序中所有单元测试文件
cookbook_test.js // cookbook单元测试文件
qunit.js // 加载程序中所有单元测试文件
我们将使用cookbook.html来做为我们应用程序的主页面文件。当然,如果你需要创建其它文件做为应用程序的主页面文件。
执行:
js jquery/generate/page cookbook index.html
或者我们直接创建一个其它文件,然后再在这个文件中添加如下代码即可。
<script type='text/javascript'
src='../path/to/steal/steal.js?cookbook'>
</script>
如果用浏览器打开cookbook.html,你将看到一个JavaScriptMVC欢迎界面。
用编辑器打开cookbook.html文件,你将看到如下代码:
<script type='text/javascript'
src='../steal/steal.js?cookbook'>
</script>
这行代码的意思是加载steal,且告诉steal加载cookbook/cookbook.js文件。
打开cookbook.js文件,你将看到如下代码:
steal(
'./cookbook.css', // Web程序的CSS样式文件
'./models/models.js', // 加载所有模型
'./fixtures/fixtures.js', // 为模型设置模拟的Ajax请求服务端
function(){ // 配置你的应用程序,这个相当于main方法
});
这个相当我们平时经常写的main函数。
这个文件实现了加载和配置你的应用程序所需要的资源。当前,它只是加载了应用程序的CSS样式文件,模型和fixture。
下面我们将创建窗口,模型,和模拟的Ajax请求服务端使我们可以创建和删除食谱。
脚手架上的食谱
我们将使用脚手架产生器快速生成:
一个在服务端具有添删查改功能的食谱模型
一个模拟的食谱服务
一个创建食谱的窗口
一个显示和删除食谱的窗口
脚手架食谱生成只需要在执行以下命令:
js jquery\generate\scaffold Cookbook.Models.Recipe
cookbook/models/recipe.js //创建一个食谱模型,它可以生成,检索,修改,和删除食谱在服务端。
cookbook/test/qunit/recipe_test.js //食谱模型单元测试文件
cookbook/models/models.js (steal added)
cookbook/test/qunit/qunit.js (steal added)
//这个产生器添加模拟的食谱模型Ajax请求代码。(你没有真正的食谱服务,如果你存在真正的服务端,可以把这部分代码删除掉。)
cookbook/fixtures/fixtures.js (code added)
//包含创建食谱的代码的文件夹
cookbook/recipe/create/create.html
cookbook/recipe/create/create.js
cookbook/recipe/create/create_test.js
cookbook/recipe/create/funcunit.html
cookbook/recipe/create/views/init.ejs
//包含显示和删除食谱的代码的文件夹
cookbook/recipe/list/funcunit.html
cookbook/recipe/list/list.html
cookbook/recipe/list/list.js
cookbook/recipe/list/list_test.js
cookbook/recipe/list/views/recipe.ejs
cookbook/recipe/list/views/init.ejs
cookbook/test/funcunit/funcunit.js (steal added)
cookbook/test/funcunit/funcunit.js (steal added)
cookbook/cookbook.js (steal added)
cookbook/cookbook.js (steal added)
cookbook/cookbook.js (code added)
页面安装
产生器执行后,你的应用程序文件(cookbook.js)如下:
steal(
'./cookbook.css', // application CSS file
'./models/models.js', // steals all your models
'./fixtures/fixtures.js', // sets up fixtures for your models
'cookbook/create',
'cookbook/list',
function(){ // configure your application
$('#recipes').cookbook_recipe_list();
$('#create').cookbook_recipe_create();
})
新添加的代码加载cookbook/create和cookbook/list模块,且在#recipes和#create元素上添加控件。
然而, #recipes和#create元素不存在。打开cookbook.html文件,添加一个#recipes的ul元素和一个#create的form元素,
代码如下:
<!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.html文件。显示如下:
下面我们继续测试厨师手册和讲解这些代码是如何工作的。
这些代码如何工作?
厨师手册程序分为5个部分:
1、食谱模块
2、食谱模拟Ajax请求
3、食谱创建控制器
4、食谱列表控件器
5、厨师手册主程序把上面4个部分放在一起
食谱模块和模拟Ajax请求(Fixture)
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
// create a recipe instance
var recipe = new Cookbook.Models.Recipe({
name: 'Hot Dog',
description: 'nuke dog, put in bun'
})
// call save to create on the server
recipe.save()
retrieve
// get recipes from the server
Cookbook.Models.Recipe.findAll({}, function(recipes){
// do something with recipes
})
update
// update the properties of a created recipe
recipe.attrs({
name: 'Bratwurst',
description: 'nuke bratwurst, put in bun'
});
// call save to send updates to the server
recipe.save()
delete
// call destroy
recipe.destroy()
当然,在这里我们没有一个真实的服务端响应请求。只能用模拟Ajax请求(Fixture)来实现。
食谱模拟Ajax请求(The Recipe Fixture)
固定装置拦截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个食谱的模拟服务。
创建食谱控制器
在浏览器打开cookbook/recipe/create/create.html文件.
这个页面演示创建食谱控制器和让你创建食谱。它是一个创建食谱程序,可以独立执行。
打开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();
}
});
});
上面代码实现使用steall加载依赖模块和创建一个Cookbook.Recipe.Create控制器。
并且为一个form元素绑定上这个Cookbook.Recipe.Create控制器。
$('form#create').cookbook_recipe_create()
当上面这句代码执行后,Cookbook.Recipe.Create控制器的init方法被调用和执行下面语句:
this.element.html(this.view());
这个代码是把cookbook/recipe/create/views/init.ejs模板渲染到控制器的元素上。并且在控制器的元素上绑定事件处理。在这个例子中,
监听的是"submit"事件在这个元素上。
当一个submit事件产生后,更新submit按钮文本,并且创建一个新的食谱。
食谱列表控制器
在浏览器打开cookbook/recipe/list/list.html.
这个页面演示Cookbook.Recipe.List控制器。它从服务端加载食谱列表,让你可以删除食谱,和它也监听食谱的创建和添加到列表中。
打开cookbook/recipe/list/list.js文件,代码如下:
$.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) );
}
});
当食谱列表控制器添加到页面上,init函数被调用:
this.element.html(this.view('init',Cookbook.Models.Recipe.findAll()) )
这个代码做了4件事:
1、从服务端请求食谱列表。
2、加载cookbook/recipe/list/views/init.ejs模板。
3、当请求和模板加载完后,渲染它。
4、把最终结果插入到列表元素中。
init.ejs 代码如下:
<%for(var i = 0; i < this.length ; i++){ %>
<li <%= this[i]%> >
<%== $.View('//cookbook/recipe/list/views/recipe.ejs', this[i] )%>
</li>
<%}%>
通过循环生成一个li元素并且把内容通过recipe.ejs子模板渲染到页面上。
我们还会发现一个很特别的地方,就是在<li>上加上<%= this[i]%>,这个地方很重要,它代表
把每个食谱模型实现绑定到对应的LI元素中。且在LI元素上设置了一个名为recipe的Class。我们一会将使用到它。
删除食谱
每个食谱都有一个删除链接。当它被点击时,列表中的'.destroy click'方法将被调用:
if(confirm("Are you sure you want to destroy?")){
el.closest('.recipe').model().destroy();
}
它找到'recipe'元素且获得该模型实例。接着执行这个模型的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]))
}
所以,当一个食谱创建后,它将渲染单个食谱init视图并且把视图加到食谱列表中。
修改食谱
When a recipe is updated, an "updated" event is triggered. The List control listens for this with:
当一个食谱修改后,一个修改事件将被触发。列表控制器监听:
"{Cookbook.Models.Recipe} updated" : function(Recipe, ev, recipe){
recipe.elements(this.element)
.html(this.view('recipe', recipe) );
}
所以,当这个食谱修改后,列表将刷新HTML中的元素。
把所有放到一起
厨师手册程序加载这个窗体并把它们加到页面上。当Cookbook.Recipe.Create创建一个食谱,它产生一个创建事件,列表监听到会把这个新创建食谱添加到列表中来。
下一节,我们讲解测试厨师手册程序。