“Безопасный” $apply в AngularJS

| Категории: Javascript, AngularJS
Анна Аминева

Иллюстрация блокнота

Если вы частенько натыкаетесь на $apply already in progress, пока работаете с ангуляром (я замечаю, что чаще всего с этим сталкиваюсь, когда внедряю сторонние плагины, которые вызывают большое количество DOM событий), то вы можете использовать сервис safeApply для проверки текущей фазы обработки в ангуляре, непосредственно перед выполнением вашей функции. Я обычно патчу $scope объект основного контроллера, а Angular распространяет мои изменения в остальные части приложения за меня:

1
2
3
4
5
6
7
8
9
10
$scope.safeApply = function(fn) {
var phase = this.$root.$$phase;
if(phase == '$apply' || phase == '$digest') {
if(fn && (typeof(fn) === 'function')) {
fn();
}
} else {
this.$apply(fn);
}
};

А затем просто заменяйте $apply на safeApply везде, где вам это необходимо:

1
2
3
$scope.safeApply(function() {
alert('Now I'm wrapped for protection!');
});

В следующем примере, вы можете увидеть как присоединять safeApply в виде Angular.JS сервиса к вашему модулю. В добавок, эта версия учитывает вызовы к $apply(), которые не передают функцию в первом аргументе.

Чтобы им воспользоваться, присоедините следующее к вашему модулю:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.factory('safeApply', [function($rootScope) {
return function($scope, fn) {
var phase = $scope.$root.$$phase;
if(phase == '$apply' || phase == '$digest') {
if (fn) {
$scope.$eval(fn);
}
} else {
if (fn) {
$scope.$apply(fn);
} else {
$scope.$apply();
}
}
}
}])

и используйте его c помощью внедрения зависимостей:

1
2
3
4
5
.controller('MyCtrl', ['$scope,' 'safeApply', function($scope, safeApply) {
safeApply($scope); // no function passed in
safeApply($scope, function() { // passing a function in
});
}])