这一篇我们将一起感受学习一个小型的、活生生的应用,而不是继续深入分析哪些单个的特性。我们将会一起感受一下,前面所讨论过的所有片段如何才能真正的组合在一起,形成一个真实的、可以运行的应用。
GutHub是一款菜谱管理应用。我们学习它有两个目的,第一是用它来管理美味的菜谱,第二是用它来学习angularjs的方方面面:
这款应用的特性如下:
a)两列布局
b)在左侧有一个导航栏
c)允许你创建新菜谱
d)允许你浏览现有的菜谱列表
主视图位于右侧,主视图将会根据具体的URL刷新,可惜显示菜谱列表、菜谱项目详情、以及用来添加或编辑菜谱的表单。
这三种东西是如何协作的,应用应该用什么样的视角来看待它们。
最有,模版代表模型的展现形式,以及用户应该如何与应用进行交互。模版主要用来做一下事情:
n展示模型
n定义用户与应用之间的交互方式
n给应用提供样式,并且判断何时以及怎样显示一些元素
n过滤并格式化数据
视图是模版和模型融合之后产生的东西。模版中不应该包含任何业务逻辑和行为,只有控制器才能具备这些特性。但是你可能会问,DOM操作应该放到那里呢?DOM操作并不会发生在控制器或模版中。它由angular指令负责(有时候也可以通过服务进行操作,DOM操作放到放到服务中可以避免重复代码)。
对于当前这款应用,我们将会让模型保持超级简单。这里的模型就是一些菜谱,它们将是真个应用的唯一的模型对象,其他所有的东西都是构建在模型之上。
每一天菜谱都具有一下特性:
就这么多,超级简单,应用中所有的东西都围绕这个简单的模型而构建,下面是一天简单的菜谱:
1{2"id":"1",3"title":"热姜汁藕片",4"description":"藕片切得越薄越容易入味。",5"ingredients":[6{7"amount":"450",8"amountUnits":"g",9"ingredientName":"姜"10},11{12"amount":"450",13"amountUnits":"g",14"ingredientName":"藕"15},16{17"amount":"0",18"amountUnits":"0",19"ingredientName":"香油"20},21{22"amount":"3",23"amountUnits":"g",24"ingredientName":"食盐"25},26{27"amount":"50",28"amountUnits":"mml",29"ingredientName":"白醋"30}31],32"instructions":"1、准备食材\n2、姜用工具磨成姜蓉\n3、姜用工具磨成姜蓉\n4、藕片入开水汆一下,大概3分钟断生,捞出\n5、锅里倒入白醋\n6、烧至沸腾后加入姜蓉\n7、30秒后加入香油和盐,将热姜汁淋入藕片上,腌制20分钟左右即可食用"3334}ViewCode
我们继续来看,围绕这个简单的模型如何构建更加复杂的UI功能。
我们要完成这个应用需要几个指令和控制器代码,然后再来看所需要的控制器。
文件位置menuSolution\app\services\services.js
1varservices=angular.module('guthub.services',['ngResource']);2services.factory('Recipe',['$resource',function($resource){3return$resource('/recipes/:id',{id:'@id'});4}]);56services.factory('MultiRecipeLoader',['Recipe','$q',function(Recipe,$q){7returnfunction(){8vardelay=$q.defer();9Recipe.query(function(recipes){10delay.resolve(recipes);11},function(){12delay.reject('无法取出食谱');13});14returndelay.promise;15};16}]);1718services.factory('RecipeLoader',['Recipe','$route','$q',function(Recipe,$route,$q){19returnfunction(){20vardelay=$q.defer();21Recipe.get(22{id:$route.current.params.recipeId},23function(recipe){24delay.resolve();25},26function(){27delay.reject('无法取出食谱'+$route.current.params.recipeId);28}29);3031returndelay.promise;32};33}]);ViewCode
Recipe.get();Recipe.save();Recipe.delete();Recipe.query();Recipe.remove();
注:如果你打算使用Recipe.delete();,并且希望在ie中使用它,你必须这样调用它Recipe[delete](),这是因为在IE中delete是一个关键字。
假设我们现在有一个菜谱对象,必要的信息都已经放在就这个对象里面了,包括Id。然后通过下面这些代码我们就可以把它保存起来:
varrecipe=newRecipe(obj);//假设id=13
recipe.$save();
以上代码会向/recipe/13路径发起一次POST请求。
其次,还有两个服务,正两个服务都是加载器:一个是单个菜单加载器,另个一是所有菜单加载器。当我们连接到路由上去,就会用到这两个加载器。它们的核心工作原理非常相似。这两个服务的工作流程如下:
Promise是一个接口,他用来处理的对象具有这样的特点:在未来的某一时刻(主要是异步调用)会从服务端返回或者被填充属性。其核心是,promise是一个带有then函数的对象.
使用promise机制的优点如下:
你可能会问,resolve(解决)方法和reject(拒绝)方法又是什么呢在angular中延迟调用是实现promise的一种方式。调用resolve方法会填充promise(也就是调用success函数),而reject方法将会调用promise的错误处理函数。
现在我们来看看应用中的指令。在目前这款应用中我们会用到如下两条指令。
Butterbar
当路由发生变化同时页面还在加载时,这一指令将会显示和隐藏信息的操作。指令将会被嵌入到路由的变化机制中,然后根据页面的状态自动隐藏或显示其标签中的内容。
Focus
Focus指令要你过来确保特定的输入项(或元素)能否获得焦点。
文件位置menuSolution\app\directives\directives.js
1vardirectives=angular.module('guthub.directives',[]);2directives.directive('butterbar',['$rootScope',function($rootScope){3return{4link:function(scope,element,attrs){5element.addClass('hide');6$rootScope.$on('$routeChangeStart',function(){7element.removeClass('hide');8});9$rootScope.$on('$routeChangeSuccess',function(){10element.addClass('hide');11});12}13}14}]);1516directives.directive('focus',function(){17return{18link:function(scope,element,sttrs){19element[0].focus();20}21};22});ViewCode
以上指令将会返回一个对象,这个对象只有一个link属性。
1、指令的处理过程分为两个步骤。在编译过程中,找到绑定在DOM元素上的指令,然后进行处理。所有的DOM操作都发生在编译阶段,在这一阶段结束之后,会产生一个内联函数。
可以像下面这样使用butterbar指令:
在一开始的时候只是简单把它隐藏起来,然后在作用域上添加两个监听器。每当路由器发生变化时,它就会显示内部元素。每当路由成功完成变化之后,它又会把butterbar隐藏起来。
另外还有一个有趣的东西是,那就是如何把$tootScope注入到指令中。所有指令都会被直接连接到angularjs的依赖注入系统中。
第二个focus指令更加简单。它实在调用当前元素上的focus()方法而已。你可以在任何元素上添加focus属性来调用它。
当页面加载完成之后,文本框会自动获取焦点。
文件位置menuSolution\app\controller\controller.js
写完指令和服务之后,终于改写控制器了,这里我们需要5个控制器。所有这些控制器都位于同一个文件夹中
你可能注意到了,edit和New这两个控制器的路由都指向了相同的模版URL——-/views/recipeForm.html,是怎么回事呢?因为我们会根据关联控制器的不同,在菜谱模版中显示不同的元素。
做完这些之后,我们来看看模版。看看这些控制器是如何把它们关联起来的,以及如何管理显示给最终用户的内容。
我们会从最外层的主模版入手,也是就index.html。他就是我们单页应用的根。其他模版都会加载到这个模版的内部。
模版位置menuSolution\app\views\
主要的模版:
在这个模版中有几个比较有趣的元素需要注意,其中大部分已经聊过。其中包括ng-app、ng-view、butterbar明显还缺少ng-controller。
现在我们来看每个控制器上的模版:
菜谱列表模版:
第二个模版:
1