初识Angular,理解它的基本设计原理能够更好的把握Angular。看了慕课网大漠穷秋老师的视频,总结一下。下面的源码也来自慕课网,能够下载到。css
MVC、模块化、双向数据绑定、指令系统。html
MVC:模型、视图、控制器,互联网的三层架构:视图负责客户端与用户交互,控制器负责中间业务逻辑的处理,互通了视图和数据,模型用语对数据的增删改查。
在大型项目中,须要多人合做开发,而每一个人只是负责其中一个功能快,MVC划分了视图、控制和模型,让指责更加清晰,也提升了复用性,同一个视图能够调用不一样的模型实现样式相同内容不一的页面(eg:不一样班级的课程表),不一样的视图也能够调用同一个模型实现同一份数据的不一样展现(eg:用三点图/折线图/雷达图... 展现同一份报告)。功能划分明确,也便于维护。
以上,NVC是为了模块化和复用,MVC只是手段,目标比手段更重要。前端
实现MVC思路是很清晰的,控制器就是大脑,负责把模型中的数据交给视图来展示,交互过程当中又经过大脑来操做数据,改变视图。
然而在前端,有一些状况须要去考虑:node
对DOM的操做,必须保证是在DOM加载完成以后执行,如何保证,MVC并未给出解决方案。程序员
资源文件之间常常会有依赖关系,如何确保依赖关系的正确性,须要程序员本身解决。ajax
js的原型继承也给前端编程带来不少困难编程
<!doctype html> <html ng-app> <head> <meta charset="utf-8"> </head> <body> <div ng-controller="HelloAngular"> <p>{{greeting.text}},Angular</p> </div> </body> <script src="js/angular-1.3.0.js"></script> <script src="HelloAngular_MVC.js"></script> </html>
js/angular-1.3.0.js 是Angular的源文件
HelloAngular_MVC.js:bootstrap
function HelloAngular($scope) { $scope.greeting = { text: 'Hello' }; }
视图很好理解,ng-controller="HelloAngular"
指定了控制器,{{greeting.text}}
指定了模型,在js代码中能够了解控制器是如何控制模型的。$scope指明了做用域,这里的做用域是div。缓存
1.不要复用controller,须要复用的地方又service实现,不良写法:架构
<div ng-controller="CommonController"> <div ng-controller="Controller1"> <p>{{greeting.text}},Angular</p> <button ng-click="test1()">test1</button> </div> <div ng-controller="Controller2"> <p>{{greeting.text}},Angular</p> <button ng-click="test2()">test2</button> <button ng-click="commonFn()">通用</button> </div> </div>
Controller:
function CommonController($scope){ $scope.commonFn=function(){ alert("这里是通用功能!"); }; } function Controller1($scope) { $scope.greeting = { text: 'Hello1' }; $scope.test1=function(){ alert("test1"); }; } function Controller2($scope) { $scope.greeting = { text: 'Hello2' }; $scope.test2=function(){ alert("test2"); } }
2.不要用controller操做DOM,由于controller操做DOM的代价很昂贵,须要重绘或者从新布局,angular中经过指令操做DOM。
3.数据的格式化操做由表单控件完成,不要用controler作数据格式化操做。
4.$filter服务能够作数据过滤,不须要用controller作数据过滤。
5.不要互相调用controller,而是经过事件来调用。
跑一下下面的代码吧:
<!doctype html> <html ng-app> <head> <meta charset="utf-8"> </head> <body> <div> <input ng-model="greeting.text"/> <p>{{greeting.text}},Angular</p> </div> </body> <script src="js/angular-1.3.0.js"></script> </html>
p元素内容会随文本框内容实时动态变动。
在加载完angular文件后,angular 经过ng-app
肯定了本身的生效范围。ng-model="greeting.text"
指定了模型,文本框的数据即是这个模型的内容,模型的做用域被绑定到了根做用域上。
和控制器的制定范围不一样,在控制器的div外面调用控制器时,是无效的,可是在div外面在调用模型时是生效的,被绑定道根做用域上全局均可访问了。
angular能够本身建立html标签,能够本身制做标签了,好开心,制做一次使用n次,即可以实现view的复用:
我想有一个hello
标签
<!doctype html> <html ng-app="MyModule"> <head> <meta charset="utf-8"> </head> <body> <hello></hello> </body> <script src="js/angular-1.3.0.js"></script> <script src="HelloAngular_Directive.js"></script> </html>
HelloAngular_Directive.js:
var myModule = angular.module("MyModule", []); myModule.directive("hello", function() { return { restrict: 'E', template: '<div>Hi everyone!</div>', replace: true } });
经过指令即可知足!指令在指令一节会细讲。
在控制器和模型中咱们都使用了做用域,$rootscope是根做用域,由ng-app
指定,同DOM树类似,做用域也是一个树形结构,本级找不到将会向上层查找,感觉一下$scope吧,这里咱们经过$scope传播事件:
<!doctype html> <html ng-app> <head> <meta charset="utf-8"> <link rel="stylesheet" type="text/css" href="Scope1.css" /> </head> <body> <div ng-controller="EventController"> Root scope <tt>MyEvent</tt> count: {{count}} <ul> <li ng-repeat="i in [1]" ng-controller="EventController"> <button ng-click="$emit('MyEvent')"> $emit('MyEvent') </button> <button ng-click="$broadcast('MyEvent')"> $broadcast('MyEvent') </button> <br> Middle scope <tt>MyEvent</tt> count: {{count}} <ul> <li ng-repeat="item in [1, 2,3]" ng-controller="EventController"> Leaf scope <tt>MyEvent</tt> count: {{count}} </li> </ul> </li> </ul> </div> </body> <script src="js/angular-1.3.0.js"></script> <script src="Scope2.js"></script> </html>
Scope2.js:
function EventController($scope) { $scope.count = 0; $scope.$on('MyEvent', function() { $scope.count++; }); }
$emit('MyEvent')能够控制控制器中事件控制本级和上级模型,
$broadcast('MyEvent')能够控制控制器中事件控制本级和下级模型,
说明:ng-repeat
代表了元素中内容重复的次数。
angular.element($0).scope()能够对$scope进行调试。
$scope的生命周期:建立-》注册监控-》检测模型变化-》观察模型是否脏-》销毁
思考一下,咱们如今会如何开发一个项目呢?一个项目会有多个文件,这些文件又该如何组织呢,一种方法是:
js文件放入scripts文件夹下,
css文件放在styles文件夹下,
img文件放在images文件夹下,
资源包放在node_modules相似的文件夹下,
嗯,彷佛看起看很工整,可是想一下,这样是否存在问题呢?
实现逻辑时,函数变量会挂在全局,这样合理吗?
这么多的功能,是否须要功能划分呢?
每一个控制器都要单独存入一个文件嘛?这样也太多了吧
把多个控制器写在同一个文件,多人开发的时候同时处理一个文件很容易产生冲突的
文件之间若是存在依赖关系,该如何解决呢?好比在开发多页应用时,要用到路由机制,路由须要调用控制器和视图,如何保证路由执行的时候,控制器和视图及其依赖的服务加载完成了呢?
这里须要借助模块化来解决问题。
模块就是实现必定功能的程序集合,这个集合中包括控制、视图、服务、过滤等。
<!doctype html> <html ng-app="HelloAngular"> <head> <meta charset="utf-8"> </head> <body> <div ng-controller="helloNgCtrl"> <p>{{greeting.text}},Angular</p> </div> </body> <script src="js/angular-1.3.0.js"></script> <script src="NgModule1.js"></script> </html>
NgModule1.js:
var helloModule=angular.module('HelloAngular', []); helloModule.controller('helloNgCtrl', ['$scope', function($scope){ $scope.greeting = { text: 'Hello' }; }]);
这样,便解决了1.变量污染全局的问题,而且能够2.按照功能划分模块。
3.4.当同一个文件加载多个控制器时,能够经过前端强大的自动化工具grunt来实现,助力了模块化的开发。
5.经过依赖注入能够解决资源依赖的问题。
首先先了解一个项目的目录,再具体讲解如何实现依赖注入。以BookStore 为例
说明:
framework存放了资源文件
tpls:templates,存放模版文件,主要用于视图的管理
index.htm为入口文件
跑一下应用:
来解析一下文件:
首先看入口文件 index.html:
<!doctype html> <html ng-app="bookStoreApp"> <head> <meta charset="UTF-8"> <title>BookStore</title> <script src="framework/1.3.0.14/angular.js"></script> <script src="framework/1.3.0.14/angular-route.js"></script> <script src="framework/1.3.0.14/angular-animate.js"></script> <script src="js/app.js"></script> <script src="js/controllers.js"></script> <script src="js/filters.js"></script> <script src="js/services.js"></script> <script src="js/directives.js"></script> </head> <body> <div ng-view> </div> </body> </html>
主要是对一些资源文件的加载,这里经过路由控制加载视图 app.js:
var bookStoreApp = angular.module('bookStoreApp', [ 'ngRoute', 'ngAnimate', 'bookStoreCtrls', 'bookStoreFilters', 'bookStoreServices', 'bookStoreDirectives' ]); bookStoreApp.config(function($routeProvider) { $routeProvider.when('/hello', { templateUrl: 'tpls/hello.html', controller: 'HelloCtrl' }).when('/list',{ templateUrl:'tpls/bookList.html', controller:'BookListCtrl' }).otherwise({ redirectTo: '/hello' }) });
bookStoreApp后面[]中指明了bookStoreApp依赖的模块,在加载时保证了依赖模块加载完以后才运行本模块,下面的配置项说明了哪一个url连接应该对应哪一个控制器管理的哪一个视图,主要when方法和otherwise方法,otherwise方法说明未指明路由是默认定向到/hello路由。
看一下这个控制器是怎样的 controllers.js:
var bookStoreCtrls = angular.module('bookStoreCtrls', []); bookStoreCtrls.controller('HelloCtrl', ['$scope', function($scope) { $scope.greeting = { text: 'Hello' }; } ]); bookStoreCtrls.controller('BookListCtrl', ['$scope', function($scope) { $scope.books =[ {title:"《Ext江湖》",author:"大漠穷秋"}, {title:"《ActionScript游戏设计基础(第二版)》",author:"大漠穷秋"}, {title:"《用AngularJS开发下一代WEB应用》",author:"大漠穷秋"} ] } ]);
其它的文件只是给了框架,为了给出一个项目的架构。
directives.js:
var bookStoreDirectives = angular.module('bookStoreDirectives', []); bookStoreDirectives.directive('bookStoreDirective_1', ['$scope', function($scope) {} ]); bookStoreDirectives.directive('bookStoreDirective_2', ['$scope', function($scope) {} ]);
filters.js:
var bookStoreFilters = angular.module('bookStoreFilters', []); bookStoreFilters.filter('bookStoreFilter_1', ['$scope', function($scope) {} ]); bookStoreFilters.filter('bookStoreFilter_2', ['$scope', function($scope) {} ]);
services.js:
var bookStoreServices = angular.module('bookStoreServices', []); bookStoreServices.service('bookStoreService_1', ['$scope', function($scope) {} ]); bookStoreServices.service('bookStoreService_2', ['$scope', function($scope) {} ]);
至此,应该了解了一个简单项目的架构和依赖管理。
终于讲到双向数据绑定了,这个神奇的东西究竟是什么?
就是视图到模型,模型到视图的双向绑定,模型和视图达成了同步。
MVC一般的思路是,将模型的数据展现在视图上,那么。如何实现从模型到视图的绑定呢?咱们彷佛实现过从模型到视图的绑定。
<!doctype html> <html ng-app> <head> <meta charset="utf-8"> </head> <body> <div ng-controller="HelloAngular"> <p><span {{greeting.text}}></span>,Angular</p> </div> </body> <script src="js/angular-1.3.0.js"></script> <script src="HelloAngular_MVC.js"></script> </html>
HelloAngular_MVC.js:
function HelloAngular($scope) { $scope.greeting = { text: 'Hello' }; }
若是使用{{greeting.text}}
,在AngularJS使用数据替换模板中的花括号,其未被渲染的模板可能会被用户看到。狂刷屏幕,视图上会暂态出现{{greeting.text}},Angular
。
还有另外一种实现方式:ng-bind="greeting.text"
。数据加载完成以前用户就不会看到任何内容。
模型到视图不只能够控制内容,还能够控制样式:
<!doctype html> <html ng-app="MyCSSModule"> <head> <meta charset="utf-8"> <link rel="stylesheet" href="NgClass.css"> </head> <body> <div ng-controller='HeaderController'> <div ng-class='{error: isError, warning: isWarning}'>{{messageText}}</div> <button ng-click='showError()'>Simulate Error</button> <button ng-click='showWarning()'>Simulate Warning</button> </div> </body> <script src="js/angular-1.3.0.js"></script> <script src="NgClass.js"></script> </html>
NgClass.css:
.error { background-color: red; } .warning { background-color: yellow; }
NgClass.js:
var myCSSModule = angular.module('MyCSSModule', []); myCSSModule.controller('HeaderController', ['$scope', function($scope) { $scope.isError = false; $scope.isWarning = false; $scope.showError = function() { $scope.messageText = 'This is an error!'; $scope.isError = true; $scope.isWarning = false; }; $scope.showWarning = function() { $scope.messageText = 'Just a warning. Please carry on.'; $scope.isWarning = true; $scope.isError = false; }; } ])
ng-class
实现了从模型到样式数据的控制,ng-class='{error: isError, warning: isWarning}
,isError
为true,则加入error类。
还有一些其它的控制,有需求时能够查阅官网。请输入代码
给一个实例,来实现双向数据绑定
咱们但愿,视图中表单的内容的改变能够影响模型,经过按钮控制的模型的改变能够影响到视图中表单的内容。
借助bootstrap实现了以上的布局:
<!doctype html> <html ng-app="UserInfoModule"> <head> <meta charset="utf-8"> <link rel="stylesheet" href="css/bootstrap-3.0.0/css/bootstrap.css"> <script src="js/angular-1.3.0.js"></script> <script src="Form.js"></script> </head> <body> <div class="panel panel-primary"> <div class="panel-heading"> <div class="panel-title">双向数据绑定</div> </div> <div class="panel-body"> <div class="row"> <div class="col-md-12"> <form class="form-horizontal" role="form" ng-controller="UserInfoCtrl"> <div class="form-group"> <label class="col-md-2 control-label"> 邮箱: </label> <div class="col-md-10"> <input type="email" class="form-control" placeholder="推荐使用126邮箱" ng-model="userInfo.email"> </div> </div> <div class="form-group"> <label class="col-md-2 control-label"> 密码: </label> <div class="col-md-10"> <input type="password" class="form-control" placeholder="只能是数字、字母、下划线" ng-model="userInfo.password"> </div> </div> <div class="form-group"> <div class="col-md-offset-2 col-md-10"> <div class="checkbox"> <label> <input type="checkbox" ng-model="userInfo.autoLogin">自动登陆 </label> </div> </div> </div> <div class="form-group"> <div class="col-md-offset-2 col-md-10"> <button class="btn btn-default" ng-click="getFormData()">获取Form表单的值</button> <button class="btn btn-default" ng-click="setFormData()">设置Form表单的值</button> <button class="btn btn-default" ng-click="resetForm()">重置表单</button> </div> </div> </form> </div> </div> </div> </div> </body> </html>
Form.js:
var userInfoModule = angular.module('UserInfoModule', []); userInfoModule.controller('UserInfoCtrl', ['$scope', function($scope) { $scope.userInfo = { email: "253445528@qq.com", password: "253445528", autoLogin: true }; $scope.getFormData = function() { console.log($scope.userInfo); }; $scope.setFormData = function() { $scope.userInfo = { email: 'damoqiongqiu@126.com', password: 'damoqiongqiu', autoLogin: false } }; $scope.resetForm = function() { $scope.userInfo = { email: "253445528@qq.com", password: "253445528", autoLogin: true }; } }
])
经过指令ng-model
实现了从$scope <-> view的双向绑定。
以前讲过用app.js实现页面的控制,可是若是一个页面内部须要局部更新,切换路由,再用$routeProvider
实现会很不合适, ui-router提供了页内的路由嵌套。
咦?为何要用页内嵌套路由呢?须要什么信息,使用ajax加载局部更新不就能够了嘛?ajax在加载后页面的url是不会改变的,也就是在加载前和加载后是同一个url,那么咱们也就没法定位到ajax加载后的页面,当想把加载后的页面加入书签或者推荐给好友时是没法得到浏览记录的。
以hello
指令为例:
<!doctype html> <html ng-app="MyModule"> <head> <meta charset="utf-8"> </head> <body> <hello></hello> <div hello></div> <div class="hello"></div> <!-- directive:hello --> <div></div> </body> <script src="framework/angular-1.3.0.14/angular.js"></script> <script src="HelloAngular_Directive.js"></script> </html>
HelloAngular_Directive.js:
var myModule = angular.module("MyModule", []); myModule.directive("hello", function() { return { restrict: 'AEMC', template: '<div>Hi everyone!</div>', replace: true }
});
restrict说明了匹配模式:
<hello></hello> <div hello></div> <div class="hello"></div> <!-- directive:hello --> <div></div>
四个hello
分别是元素、属性、样式类和注释的匹配模式。
template模版指定了指令的内容。template: '<div>Hi everyone!</div>'
直接指定了模版的内容,若是模版内容不少饿时候在js里面写标签是很痛苦的。
`templateUrl: 'hello.html'能够经过指定url来指定一个文件为模版,
还能够经过缓存的方式存储复用模版:
var myModule = angular.module("MyModule", []); //注射器加载完全部模块时,此方法执行一次 myModule.run(function($templateCache){ $templateCache.put("hello.html","<div>Hello everyone!!!!!!</div>"); }); myModule.directive("hello", function($templateCache) { return { restrict: 'AECM', template: $templateCache.get("hello.html"), replace: true } });
当replace
为true时,
若是hello
中嵌套了内容,hello中的内容会被替换掉,不显示:
<hello> <div>这里是指令内部的内容。</div> </hello>
若是不想被替换,可使用transclude:true
:
var myModule = angular.module("MyModule", []); myModule.directive("hello", function() { return { restrict:"AE", transclude:true, template:"<div>Hello everyone!<div ng-transclude></div></div>" } });
当指令想要嵌套指令时,这种方式能够不让被嵌套的指令被替代掉。以上大概讲解了一下指令,还有不少指令的内容,在基础入门这里就先介绍这么多。