初識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>" } });
當指令想要嵌套指令時,這種方式能夠不讓被嵌套的指令被替代掉。以上大概講解了一下指令,還有不少指令的內容,在基礎入門這裏就先介紹這麼多。