angularjs實踐

整個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>的方式

相關文章
相關標籤/搜索