整個angular的開發過程都是可使用yeoman構建的。譬如如下的命令。以coffescript形式生成angular的各個組件javascript
yo angular:route myroute --coffee
yo angular:controller user --coffee
yo angular:directive myDirective --coffee
yo angular:filter myFilter --coffee
yo angular:view user --coffee
yo angular:service myService --coffee
yo angular:decorator serviceName --coffeehtml
理解ngModel中的$parsers和$formattershtml5
formatters改變model中的值如何顯示在view上java
parsers改變view上的如何存儲在model中jquery
下面是一個例子angularjs
//format text going to user (model to view) ngModel.$formatters.push(function(value) { return value.toUpperCase(); }); //format text from the user (view to model) ngModel.$parsers.push(function(value) { return value.toLowerCase(); });
在下面的地址中查看: http://plnkr.co/UQ5q5FxyBzIeEjRYYVGXweb
<input type="button" value="set to 'misko'" ng-click="data.name='misko'"/> <input type="button" value="set to 'MISKO'" ng-click="data.name='MISKO'"/> <input changecase ng-model="data.name" />
When you type a name in (view to model), you will see that the model is always lowercase. But, when you click a button and programatically change the name (model to view), the input field is always uppercase.ajax
$apply & $digest應該何時使用:express
官方給出了明確的答案,那就是在數據模型的改變不是經過angularjs自身的api來調用的時候就須要咱們手動調用$apply或者$digest,例如:api
setTimeout(function(){scope.name='ken';},1000);
這個時候模型數據改變了,可是不會反映到視圖中,因此須要咱們手動添加$apply,能夠像下面這樣:
setTimeout(function(){scope.$apply(scope.name='ken';);},1000);
或者也可使用$timeout,在angularjs提供的$timeout裏面會幫咱們自動調用$apply;
一段代碼弄清楚$apply跟$digest的關係
1 Scope.prototype.$apply = function(expr) { 2 try { 3 this.$beginPhase("$apply"); 4 return this.$eval(expr); 5 } finally { 6 this.$clearPhase(); 7 this.$digest(); 8 } 9 };
$interpolate的使用方式:
var getFullName = $interpolate('{{first}}{{last}}');
var scope = { first:'Pete',last:'Bacon Darwin' };
var fullName = getFullName(scope);
綁定驗證反饋信息
爲了在咱們的字段模板中顯示錯誤信息,咱們能夠像下面這樣作:
<span class="help-inline" ng-repeat="error in $fieldErrors">
{{$validationMessages[error](this)}}
</span>
this指向的是當前的scope
在angularjs的routes中使用resolve
.when('/admin/users/:userid', {
templateUrl: 'tpls/users/edit.html'
controller: 'EditUserCtrl',
resolve: {
user: function($route, Users) {
return Users.getById($route.current.params.userid);
}
}
})
這裏resolve的做用是返回內容提供給controller,返回的內容能夠經過參數的形式傳遞到controllerz中
.controller('EditUserCtrl', function($scope, user){
$scope.user = user;
...
})
對於這種模式有一個益處,就是咱們能夠設計不一樣的路由來對應同一個controller,只要resolve中返回不一樣的值就能夠了
$routeProvider.when('/admin/users/new', {
templateUrl:'admin/users/users-edit.tpl.html',
controller:'UsersEditCtrl',
resolve:{
user: function (Users) {
return new Users();
}
}
});
$routeProvider.when('/admin/users/:userId', {
templateUrl:'admin/users/users-edit.tpl.html',
controller:'UsersEditCtrl',
resolve:{
user: function ($route, Users) {
return Users.getById($route.current.params.userId);
}
}
});
使用ng-include來處理多UI模板的狀況
$routeProvider.when('/admin/users/new', {
templateUrl:'admin/admin.tpl.html',
contentUrl:'admin/users/users-edit.tpl.html',
menuUrl:'admin/menu.tpl.html',
controller:'UsersEditCtrl',
...
});
在頁面中,咱們就能夠經過$route.current獲得對應的模板URL了
<div>
<div ng-include='$route.current.contentUrl'>
<!--menu goes here -->
</div>
<div ng-include='$route.current.menuUrl'>
<!--content goes here -->
</div>
</div>
如何加快$digest循環
1.減小$watch的表達式取值時間
$watch的用法以下:$scope.$watch(watchExpression, modelChangeCallback);
咱們應當儘可能減小watchExpression的計算時間
2.減小console.log()的使用
對好比下兩段代碼
$scope.getName = function () {
return $scope.name;
};
$scope.getNameLog = function () {
console.log('getting name');
return $scope.name;
};
他們所花費的時間對比:
所以,咱們應該儘可能減小在產品階段的代碼中使用console.log打印日誌
3.儘可能避免在watch-expression中訪問DOM
引用「mastering web application development with angularjs中的一段話:
Any DOM operation is slow and computed properties are extra slow.
The real problem is that DOM is implemented in C++. It turns out that
it is very expensive to call a C++ function from JS. So any DOM access
is magnitudes slower than JavaScript object access.
4.限制watches的執行數量
可經過刪除沒必要要的watch來實現
從新審視UI,在佈局上是否夠輕量級,有沒有必要存在大量的DOM,咱們應該化繁爲簡
慎重使用watch來顯示隱藏DOM,例如當咱們使用ng-show來隱藏某個DOM的時候,若是DOM裏面綁定了某個模型數據,在每次input變化的時候模型數據都會變化,$digest循環都會對其進行計算,所以而浪費資源,在這種時候咱們使用ng-switch可能會更加適合。
5.刪除再也不使用的watches
var watchUnregisterFn = $scope.$watch('name', function (newValue,
oldValue) {
console.log("Watching 'name' variable");
...
});
//later on, when a watch is not needed any more:
watchUnregisterFn();
如上代碼所示,$scope.$watch()返回一個函數,這個函數能夠用來取消咱們的監控,只要將這個函數執行一次便可。
6.減小$digest的使用頻率
相似於作一個定時器,每秒鐘更新一下時間,咱們會用到timeout, 但更好是使用angularjs給咱們提供的$timeout服務,代碼以下:
1 .directive('clock', function ($timeout, dateFilter) { 2 return { 3 restrict: 'E', 4 link: function (scope, element, attrs) { 5 function update() { 6 // get current time, format it and update DOM text 7 element.text(dateFilter(new Date(), 'hh:mm:ss')); 8 //repeat in 1 second 9 $timeout(update, 1000); 10 } 11 update(); 12 } 13 }; 14 })
可是這樣有一個問題,每過一秒中,$digest都會執行一次,還好angularjs給咱們的$timeout提供了第三個參數來決定是否調用$digest,代碼能夠改成:
function update() {
element.text(dateFilter(new Date(), 'hh:mm:ss'));
$timeout(update, 1000, false);
}
7.不管何時都應該避免深度的watch
例若有這樣一個user對象:
$scope.user = {
firstName: 'AngularJS',
lastName: 'Superhero',
age: 4,
superpowers: 'unlimited',
// many other properties go here…
};
咱們能夠經過以下方式來實現深度監控,就是在$watch中傳入第三個值爲true的參數
$scope.$watch('user', function (changedUser) {
$scope.fullName =
changedUser.firstName + ' ' + changedUser.lastName;
}, true);
可是這種方式很是不友好,佔用內存,計算複雜,咱們可使用更好的方式:
$scope.$watch(function(scope) {
return scope.user.firstName + ' ' + scope.user.lastName;
}, function (newFullName) {
$scope.fullName = newFullName;
});
還有另一種避免使用$watch的方式,只須要咱們修改模板便可
在模板中綁定方法,{{ fullName() }}
而後controller中定義方法
$scope.fullName = function () {
return $scope.user.firstName + ' ' + $scope.user.lastName;
};
8.分析被watch的expression表達式
分析以下一段代碼:
<p>This is very long text that refers to one {{variable}} defined on a
scope. This text can be really, really long and occupy a lot of space
in memory. It is so long since… </p>
在angularjs中,angularjs會將整段代碼保存到內存中,而不只僅是{{variable}}這個變量,若是咱們但願真個表達式變得更短,消耗內存更小,咱們能夠給變量添加一個標籤:
<p>This is very long text that refers to one <span ngbind='variable'></span> defined on a scope. This text can be really,
really long and occupy a lot of space in memory. It is so long since…
</p>
如何添加依賴
咱們能夠很簡單的注入依賴就像下面這樣
angular.module('projects', []).controller('ProjectCtrl', function($scope){//todo...});
可是這樣存在一個後期隱患,由於代碼壓縮後,咱們就沒法得知傳入的參數是什麼了,$scope可能會變成a或者b;
所以咱們須要換一種方式來添加依賴
angular.module('projects',[]).controller('ProjectCtrl', ['$scope', function($scope){//todo}]);
還有config以及run方法咱們均可以用一樣的方法來添加依賴
angular.module('app').config(['$routeProvider', '$localtionProvider', function($routeProvider, $locationProvider){
$locationProvider.html5Mode(true);
$routeProvider.otherwise({redirectTo: '/projectsinfo'});
}]);
angular.module('app').run(['security', function(security){
security.requestCurrentUsesr();
}]);
其餘provider, derective依葫蘆畫瓢
預加載模板技術
通常的,模板的加載過程會消耗一個網絡延時,angularjs給咱們提供了兩種解決方案來實現預加載模板
第一種方法是使用script標籤來預加載模板
將模板用script標籤嵌套起來,添加angularjs可識別的屬性,另外,該模板須要寫在ng-app範圍以內;
<script type="text/ng-template" id="tpls/users/list.html">
<div class="hello">hello world</div>
</script>
應該注意一點,id值與咱們的template url一致
<html ng-app> <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.2/angular.js"></script> <body> <div ng-include='"test"'></div> <script type="text/ng-template" id="test"> This is the content of the template </script> </body> </html>
ng-include的值必須用'""'或者"''",只有一個單引號或者雙引號的時候會無效
第二種方法是使用$templateCache服務
var myApp = angular.module('myApp', []); myApp.run(function($templateCache) { $templateCache.put('templateId.html', 'This is the content of the template'); });
經過下面的方式來倒入模板
<div ng-include=" 'templateId.html' "></div>
或者也能夠經過javascript的方式拿到模板
$templateCache.get('templateId.html');
理解$compile
angularjs中的$compile服務能夠提供編譯服務,若是是$compile()這樣使用的話會返回一個link方法,例如
var link = $compile('<p>hello {{name}}</p>');
再傳入一個scope的話就能將dom跟scope進行鏈接返回一個angular element;返回的element咱們能夠插入DOM樹中
代碼片斷,將模板保存在$templateCache中:
hello.run(function($templateCache) {
$templateCache.put('templateId.html', '<a>this is the content of the template{{name}}</a>');
});
上面的代碼須要注意一點,保存到$templateCache中的模板必須有標籤包圍,不然報錯,例如上面有a標籤包圍;
代碼片斷,從$templateCache中獲取模板並編譯插入DOM樹中:
$scope.showmyhtml = function() {
//$scope.myhtml = $templateCache.get('templateId.html');
var element = $compile($templateCache.get('templateId.html'))($scope);
console.log(element);
angular.element(document.querySelector('#myhtml')).append(element);
}
注意一點的是angularjs中自帶的jqlite不能想jquery那樣直接經過$符號來使用,能夠經過angular.element來將dom轉化爲jq對象;
angular裝飾者decorator
decorator在$provider的命名空間下面,該方法傳遞兩個參數,一個須要裝飾的對象,一個是裝飾函數體
1 var Mail = function() { 2 this.receiver = ''; 3 this.body = ''; 4 this.cc = []; 5 }; 6 7 Mail.prototype.setReceiver = function(receiver) { 8 this.receiver = receiver; 9 }; 10 11 Mail.prototype.setBody = function(body) { 12 this.body = body; 13 }; 14 15 angular.module('A', []).service('Mail', Mail);
1 angular.module('B', ['A']).config(function($provide) { 2 $provide.decorator('Mail', function($delegate) { 3 $delegate.addCC = function(cc) { 4 this.cc.push(cc); 5 }; 6 return $delegate; 7 }); 8 }) 9 .controller('TestCtrl', function($scope, Mail) { 10 Mail.addCC('jack'); 11 console.log(Mail); 12 });
angular的調試
經常使用到的調試工具相似於console.log(), angular提供了$log服務
1 angular.module('app', []) 2 3 .controller('MainCtrl', ['$log', function($log){ 4 $log.debug('Hello Debug!'); 5 }]);
固然咱們能夠設置是否打開日誌的功能,在config中進行配置:
1 angular.module('app', []) 2 3 .config(['$logProvider', function($logProvider){ 4 $logProvider.debugEnabled(false); 5 }]) 6 7 .controller('MainCtrl', ['$log', function($log){ 8 $log.debug('Hello Debug!'); 9 }])
同時,咱們還能夠利用上面說到的decorator來對$log進行裝飾:
1 angular.module('app', []) 2 3 .config(['$provide', function ($provide) { 4 $provide.decorator('$log', ['$delegate', function ($delegate) { 5 // Keep track of the original debug method, we'll need it later. 6 var origDebug = $delegate.debug; 7 /* 8 * Intercept the call to $log.debug() so we can add on 9 * our enhancement. We're going to add on a date and 10 * time stamp to the message that will be logged. 11 */ 12 $delegate.debug = function () { 13 var args = [].slice.call(arguments); 14 args[0] = [new Date().toString(), ': ', args[0]].join(''); 15 16 // Send on our enhanced message to the original debug method. 17 origDebug.apply(null, args) 18 }; 19 20 return $delegate; 21 }]); 22 }]) 23 24 .controller('MainCtrl', ['$log', function ($log) { 25 $log.debug('Hello Debug!'); 26 }]);
內存管理的重要性
angularjs在銷燬一個scope和把一個scope從它的父級移除以前會廣播一個$destroy事件,監聽這個事件對清理任務和資源很重要,例如一個timeout的例子:
1 module.controller("MyController", function($scope, $timeout) { 2 var timeout = function() { 3 $scope.value += 1; 4 $timeout(timeout, 100); 5 }; 6 $timeout(timeout, 100); 7 $scope.value = 0; 8 9 });
若是用戶來回導航到一個view來加載這個controller,那每次導航將會添加另外一個永遠運行的計時器,監聽$destroy,咱們能夠取消掉timeout來移除資源的消耗:
1 module.controller("MyController", function($scope, $timeout) { 2 var timeout = function() { 3 $scope.value += 1; 4 timer = $timeout(timeout, 100); 5 }; 6 7 var timer = $timeout(timeout, 100); 8 $scope.value = 0; 9 $scope.$on("$destroy", function() { 10 11 if (timer) { 12 $timeout.cancel(timer); 13 } 14 }); 15 });
angularjs中對img的src屬性應該使用ng-src來代替
angularjs中應該儘量用promise來處理回調
第三方的回調應該使用$apply來包裹,以便通知angularjs關於環境的變化
若是咱們不想讓用戶在angularjs未加載以前顯示html,可使用ng-cloak來隱藏html
<div class="ng-cloak">...... .ng-cloak{display: none;}
angularjs加載完後會將.ng-cloak變爲block;
編寫咱們本身的directive,最好的實踐不是使用ng前綴,可使用相似於<my-component>的方式