Дайджест не запускается, когда переменная области видимости обновляется в AngularJS

Я использую AngularJS и angularFire, чтобы показать список моих задач:

<ul ng-repeat="tool in tools">
    <li>{{tool.name}} {{ tool.description}}</li>
</ul>

var toolRef = new Firebase(dbRef + "/tools/" + toolId);
toolRef.once('value', function(snapshot) {
      console.log(angular.toJson(snapshot.val()));
      $scope.tools.push(snapshot.val());
      //$scope.$apply();
});

http://jsfiddle.net/oburakevych/5n9mj/11/

Код очень прост: я привязываю объект «сайт» к своей базе данных Firebase. Объект содержит список идентификаторов соответствующих инструментов. Затем я загружаю каждый инструмент в переменную $scope.tools и использую ng-repeat, чтобы показать их в пользовательском интерфейсе. Однако каждый раз, когда я добавляю новую запись в свой $scope.tools, DOM не обновляется. Я должен вызывать $scope.$apply для запуска дайджеста после каждого нажатия — см. закомментированную строку 18, и тогда это работает.

Это действительно странно, так как я сею это несколько раз и только с переменными области видимости, связанными с angularFire.

Кто-нибудь может это объяснить? Я делаю что-то не так?


person Alexander Burakevych    schedule 20.08.2013    source источник


Ответы (4)


Я не уверен в методе Firebase once, как он работает, но похоже, что он вносит изменения в модель вне контекста Angular в своем обратном вызове, и, следовательно, вам нужен $scope.$apply.

В угловых сервисах, таких как $http , $resource и $timeout, внутренне вызывают $scope.$apply, поэтому вам не нужно вызывать его при обратном вызове. Если система Firebase предоставляет замену метода для once, который внутренне применяется или возвращает промис, вы вполне можете пропускать вызов метода apply снова и снова.

person Chandermani    schedule 20.08.2013
comment
Стоит отметить, что если ему действительно нужно использовать $apply, это должно быть $scope.$apply(function() { //do stuff }); ссылка docs.angularjs.org/api/ng.$rootScope.Scope - person ivarni; 20.08.2013
comment
Да, вы правы, изменение выходит за рамки angular и применяется сторонней библиотекой. - person Alexander Burakevych; 21.08.2013

Поскольку вы меняете область вне AngularJS, вы должны использовать $scope.$apply(), чтобы информировать Angular об изменениях области. Это обычный способ справиться с этим. Чтобы использовать обработку ошибок AngularJS, я бы обернул код в функцию обратного вызова, например:

$scope.$apply(function(){
  $scope.tools.push(snapshot.val());
});
person tschiela    schedule 20.08.2013

Как говорит Чиела, вам нужно обернуть функцию. Я использую $timeout. Так...

<ul ng-repeat="tool in tools">
    <li>{{tool.name}} {{ tool.description}}</li>
</ul>

var toolRef = new Firebase(dbRef + "/tools/" + toolId);
toolRef.once('value', function(snapshot) {
      $timeout(function() {
          $scope.tools.push(snapshot.val());
      })
});
person johnsoftek    schedule 20.08.2013

Чтобы обновлять области видимости при их изменении, вам нужно использовать фабрику angularFireCollection angularFire. В противном случае вы просто вызываете класс javascript, который не будет обновлять области до $apply().

var getTool = function(dbRef,toolId) {
    console.log("Gettingtool ID: " + toolId);
    angularFireCollection(dbRef + "/tools/" + toolId, function(snapshot) {
          console.log(angular.toJson(snapshot.val()));
          $scope.tools.push(snapshot.val());
    });
};

ДЕМО

person L105    schedule 20.08.2013