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

相关文章
相关标签/搜索