2013-06-23 3 views
1

У меня есть компонент пользовательского интерфейса, который имеет обратную связь $watch по своей ширине (причина не имеет отношения к этому сообщению). Проблемы заключается в том, что в некоторых случаях:

  • Ширина изменяется от не углового контекста ->
  • Там нет $digest цикла ->
  • Моего $watch обратного вызова не вызывается.

Хотя мое приложение является полным угловым приложением, все еще есть случаи, когда код выполняется в неконкурентном контексте. Например: JQuery calles window.setTimeout - так что даже если мой код под названием JQuery из углового контекста, вызов обратного вызова timeout вызывается в неконкурентном контексте, и мой обратный вызов $watch не будет выполнен после этого.

Кстати, даже угловой сами называют window.setTimeout в их AnimatorService ...

Так что мой вопрос: Как я могу убедиться, что $digest цикла всегда выполняется после того, как любой код выполняется? (Даже если код является третьим участником код ...)

Я думал о переопределении оригинальный метод window.setTimeout но:

  • Он чувствует себя немного некрасиво и опасно.
  • Я боюсь, что он не будет охватывать все варианты использования.

Добавление plunker. Образец plunker содержит:

  • Элемент, который может быть скрыт с помощью JQuery fadeOut метод.
  • Кнопка, которая выполняет вызов fadeOut для сокрытия элемента.
  • Текст, отображающий статус отображения элемента (Shown!!! или Hidden!!!). Этот текст обновляется $watch ing на элементе display Недвижимость.
  • Кнопка, которая ничего не делает, кроме как инициировать некоторый угловой код, чтобы вызвать цикл $digest.

Flow:

  • Нажмите кнопку Fade Out -> элемент будет скрыт, но текст статуса останется Shown!!!. Вы можете ждать вечность сейчас - или:
  • Нажмите кнопку Do Nothing -> текст будет меняться.

Почему?

При нажатии на кнопку Fade OutJQuery.fadeOut вызывается метод window.setTimeout. После этого вызывается мой обратный вызов $watch, но элемент все еще не скрыт. Элемент скрыт только после вызова обратного вызова таймаута, но тогда нет цикла $digest (и у меня нет способа, которым я знаю, чтобы вызвать его). Только в следующий раз, когда будет запущен угловой код, функция $watch будет вызвана снова, и статус будет обновлен.

+0

Почему [$ scope. $ Apply()] (http://docs.angularjs.org/api/ng.$rootScope.Scope#$apply) не решение? – Stewie

+0

Не совсем - проблема в том, что я не могу получить какое-либо событие, когда этот код работает. Как и в примере, если какая-то сторонняя сторона (например, JQuery или Angular) вызывает 'window.setTimeout', мой код не будет выполняться, когда вызывается обратный вызов таймаута, поэтому я не могу добавить вызов' $ scope.apply'. –

+0

Боюсь, вам придется настроить демонстрацию плункера, потому что ваше дело звучит довольно необычно. – Stewie

ответ

1

AngularJS предоставляет специальный метод $apply объекта scope, который позволяет выполнять код вне рамок системы AngularJS.

Функция $apply выполнит ваш код в правильном контексте и применит цикл $digest, поэтому вам не придется иметь дело с этим самостоятельно.

Для его реализации в вашем коде, вы можете:

// Get element 
var $element = $('#yourElement'); 

// Get scope 
var scope = angular.element($element).scope(); 

// Execute your code 
scope.$apply(function(scope){ 
    // Your logic here 
    // All watchers in the scope will be triggered 
}); 

(Сценарий выше, может изменяться в зависимости от фактического применения).

Вы можете прочитать больше о $apply метода объекта области видимости прямо здесь: http://docs.angularjs.org/api/ng.$rootScope.Scope

Надежда, что помогает!

+0

Привет. Спасибо за подробный ответ. Однако, боюсь, это не поможет. У меня нет способа добавить $ apply call, так как функция, делающая изменение, не моя (это сторонняя сторона -> JQuery). Пожалуйста, см. Плункер, который я добавил. –

0

Глядя на ваш plunker, вы можете добавить функцию обратного вызова на вызов анимировать вручную инициировать обновление углового масштаба, как только анимация завершена:

До:

$scope.fadeOut = function() { 
    animatedElement.fadeOut('slow'); 
}; 

var getDisplay = function() { 
    return animatedElement.css('display'); 
}; 

$scope.$watch(getDisplay, function(display) { 
    console.log('$watch called with display = `' + display + '`'); 
    $scope.display = display === 'none' ? 'Hidden!!!' : 'Shown!!!'; 
}); 

После:

$scope.fadeOut = function() { 
    animatedElement.fadeOut('slow', function() { $scope.$digest(); }); 
}; 

Это приведет к тому, что ваши часы на getDisplay будут вызываться, когда анимация будет завершена, и в это время она вернет правильное значение.

Смежные вопросы