Javascript AngularJS - ng-bind 不更新

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/30119538/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 04:41:58  来源:igfitidea点击:

AngularJS - ng-bind not updating

javascriptangularjs

提问by Alex Ryans

I have a controller which has a function to get some alerts from an API and update a count on the front-end of my site which is bound to the alert.

我有一个控制器,它具有从 API 获取一些警报并更新绑定到警报的站点前端的计数的功能。

Unfortunately the ng-bindattribute I'm using doesn't seem to be updating the count live, even though a simple console.log()is telling me that the actual alert count is being updated in the controller.

不幸的是,ng-bind我使用的属性似乎并没有实时更新计数,即使一个简单的console.log()方法告诉我实际警报计数正在控制器中更新。

Front-end

前端

<div class="modeSelector modeSelector_oneUp" data-ng-controller="MyLivestockController as vm">
    <a class="modeSelector-mode" data-ui-sref="my-livestock">
        <div class="modeSelector-type">Alerts</div>

        <img class="modeSelector-icon" src="/inc/img/_icons/envelope-black.svg" onerror="this.src=envelope-black.png" />

        <span data-ng-bind="vm.alertCount"></span>
    </a>
</div>

Controller

控制器

(function() {

  'use strict';

  function MyLivestockController(userService) {

    var vm = this;

    vm.myLivestockNotification = {
      isLoading: true,
      hasError: false
    };

    vm.alertsNotification = {
      isLoading: true,
      hasError: false,
      hasData: false
    };

    vm.deleteAlert = function(id) {

      vm.currentAlert = void 0;
      vm.alertsNotification.isLoading = true;

      userService.deleteAlert(vm.user.id, id).then(function() {

        // Remove the alert from our Array
        vm.alerts = vm.alerts.filter(function(alert) {
          return alert.id !== id;
        });

        // Refresh the alert count for the user
        vm.getAlerts(vm.user.id);

        vm.alertsNotification.isLoading = false;
        vm.alertsNotification.hasError = false;
      }, function() {
        vm.alertsNotification.hasError = true;
      });
    };

     vm.getAlerts = function(id) {
        userService.getAlerts(id).then(function(alertData) {
          vm.alertCount = alertData.length;

          if (vm.alertCount > 0) {
            vm.alertsNotification.hasData = true;
          } else {
            vm.alertsNotification.hasData = false;
          }

          vm.alerts = alertData;

          vm.alertsNotification.isLoading = false;
          vm.alertsNotification.hasError = false;
        }, function() {
          vm.alertsNotification.hasError = true;
        });
    };

    // Init
    (function() {
      userService.getCurrentUser().then(function(data) {
        vm.myLivestockNotification.hasError = false;
        vm.myLivestockNotification.isLoading = false;

        vm.user = data;

        // Get alert count for the user
        vm.getAlerts(vm.user.id);
      }, function() {
        vm.myLivestockNotification.hasError = true;
      });
    })();
  }

  angular
    .module('abp')
    .controller('MyLivestockController', MyLivestockController);

})();

Service

服务

(function() {

  'use strict';

  function userService($q, $sessionStorage, $localStorage, $filter, user) {

    var service = this;

    service.getAlerts = function(id) {
      var deferred = $q.defer();

      user.alerts({ userID: id }, function(response) {
        if (response.hasOwnProperty('data')) {

          // Convert dates to valid Date
          angular.forEach(response.data, function(alert) {
            /* jshint camelcase: false */
            if (alert.created_at) {
              alert.created_at = $filter('abpDate')(alert.created_at);
            /* jshint camelcase: true */
            }
          });

          deferred.resolve(response.data);
        }
        else {
          deferred.reject('DATA ERROR');
        }
      }, function(e) {
        deferred.reject(e);
      });

      return deferred.promise;
    };

  angular
    .module('abp')
    .service('userService', userService);

})();

As you can see, I've got my getAlerts()function being called every time an alert is deleted, using the deleteAlert()function, but the <span data-ng-bind="vm.alertCount"></span>on the front-end only updates after refreshing the page, where I'd like it to update live.

如您所见,getAlerts()每次删除警报时都会调用我的函数,使用该deleteAlert()函数,但<span data-ng-bind="vm.alertCount"></span>前端的 仅在刷新页面后更新,我希望它实时更新。

回答by Daniel Gruszczyk

Your bind is not updating because you change the value of alertCount outside of digest cycle of your angular app. When you refresh your app, the digest runs and thus your value gets updated. Wrap the update of the variable in $scope.apply() like so:

您的绑定未更新,因为您在 angular 应用程序的摘要周期之外更改了 alertCount 的值。当您刷新应用程序时,摘要会运行,因此您的值会更新。将变量的更新包装在 $scope.apply() 中,如下所示:

$scope.$apply(function(){
    vm.alertCount = alertData.length;
});

This will force digest and update the value live.
If you have more values that are updated outside of digest (any callback, promise etc) you can force digest cycle by calling:

这将强制消化并实时更新值。
如果您有更多在摘要之外更新的值(任何回调、承诺等),您可以通过调用强制摘要循环:

$scope.$apply();

Hope it helps.

希望能帮助到你。

EDIT -----
Given your update with full code, I see that you are not injecting scope anywhere in your controller, the controllers I write usually start like that:

编辑 -----
鉴于您对完整代码的更新,我看到您没有在控制器中的任何地方注入范围,我编写的控制器通常是这样开始的:

(function () {

var app = angular.module('mainModule');

app.controller('myController', ['$scope', '$myService', function ($scope, $myService) {

    //logic
}]);
}());

EDIT -----
Here is a quick go I had on your code:

编辑 -----
这是我对您的代码的快速操作:

(function() {
'use strict';


var app = angular.module('abp');

app.controller('MyLivestockController', ['$scope', 'userService', function($scope, userService) {

    var vm = {};
    $scope.vm = vm;

    vm.myLivestockNotification = {
        isLoading: true,
        hasError: false
    };

    vm.alertsNotification = {
        isLoading: true,
        hasError: false,
        hasData: false
    };

    vm.deleteAlert = function(id) {

        vm.currentAlert = void 0;
        vm.alertsNotification.isLoading = true;

        userService.deleteAlert(vm.user.id, id).then(function() {

            // Remove the alert from our Array
            vm.alerts = vm.alerts.filter(function(alert) {
                return alert.id !== id;
            });

            // Refresh the alert count for the user
            vm.getAlerts(vm.user.id);

            vm.alertsNotification.isLoading = false;
            vm.alertsNotification.hasError = false;
        }, function() {
            vm.alertsNotification.hasError = true;
        });
    };

    vm.getAlerts = function(id) {
        userService.getAlerts(id).then(function(alertData) {
            vm.alertCount = alertData.length;

            if (vm.alertCount > 0) {
                vm.alertsNotification.hasData = true;
            } else {
                vm.alertsNotification.hasData = false;
            }

            vm.alerts = alertData;

            vm.alertsNotification.isLoading = false;
            vm.alertsNotification.hasError = false;

            //important, this is promise so we have to apply the scope to update view
            $scope.$apply();
        }, function() {
            vm.alertsNotification.hasError = true;
        });
    };

    // Init
    (function() {
        userService.getCurrentUser().then(function(data) {
            vm.myLivestockNotification.hasError = false;
            vm.myLivestockNotification.isLoading = false;

            vm.user = data;

            // Get alert count for the user
            vm.getAlerts(vm.user.id);
        }, function() {
            vm.myLivestockNotification.hasError = true;
        });
    })();
}]);

})();

The general idea is:

总体思路是:

  1. you create an app (angular.module)
  2. you create a controller in this app, with $scope injected
  3. any values you want to be updated on your view, you add to $scope
  4. if you have any $scope updates in a callback, event or promise, you wrap them in (or follow with) $scope.$apply call
  1. 你创建一个应用程序(angular.module)
  2. 你在这个应用程序中创建了一个控制器,注入了 $scope
  3. 您希望在视图中更新的任何值,都添加到 $scope
  4. 如果您在回调、事件或承诺中有任何 $scope 更新,则将它们包装在(或跟随)$scope.$apply 调用中

I think this should work for you :)

我认为这应该对你有用:)

回答by JcT

I have attempted to reproduce your code below with a mock userService, and some slight modifications to the html view so we can more clearly see the alerts and delete them. I have not modified your Controller.

我尝试使用模拟 userService 重现您的代码,并对 html 视图进行了一些细微修改,以便我们可以更清楚地看到警报并删除它们。我没有修改你的控制器。

This appears to work, yes?

这似乎有效,是吗?

Which leads me to believe there may be some issue with the implementation of your userService. If you are able to post the relevant code, I can update this answer with a clarified solution.

这让我相信您的 userService 的实现可能存在一些问题。如果您能够发布相关代码,我可以使用明确的解决方案更新此答案。

UPDATE:As you've updated your question with the userServicecode, I've updated the below to more closely match. I still have a mock service standing in place of the userdependency of the userService. Additionally I made a couple of small edits to the Controller class so that while promises are still resolving we can see 'Updating...' in place of the alerts count.

更新:当你用userService代码更新你的问题时,我已经更新了下面的内容以更紧密地匹配。我仍然有一个模拟服务站在地方的user的依赖userService。此外,我对 Controller 类进行了一些小的编辑,以便在承诺仍在解析时我们可以看到“正在更新...”代替警报计数。

This all still appears to work, unless I'm misunderstanding - will think on it more and update this 'answer' when I can think of where else to investigate for the source of the issue, see if we can at least reproduce it!

这一切似乎仍然有效,除非我误解了 - 当我想到其他地方可以调查问题的根源时,我会更多地思考并更新这个“答案”,看看我们是否至少可以重现它!

(function() {

  'use strict';

  function MyLivestockController(userService) {

    var vm = this;

    vm.myLivestockNotification = {
      isLoading: true,
      hasError: false
    };

    vm.alertsNotification = {
      isLoading: true,
      hasError: false,
      hasData: false
    };

    vm.deleteAlert = function(id) {

      vm.currentAlert = void 0;
      vm.alertsNotification.isLoading = true;

      return userService.deleteAlert(vm.user.id, id).then(function() {

        // Remove the alert from our Array
        vm.alerts = vm.alerts.filter(function(alert) {
          return alert.id !== id;
        });

        // Refresh the alert count for the user
        vm.getAlerts(vm.user.id).then(function() {
          vm.alertsNotification.isLoading = false; //put here, loading isn't really finished until after .getAlerts() is done
          vm.alertsNotification.hasError = false;
        });

      }, function() {
        vm.alertsNotification.hasError = true;
      });
    };

    vm.getAlerts = function(id) {

      vm.alertsNotification.isLoading = true;

      return userService.getAlerts(id).then(function(alertData) { //return the promise so we can chain .then in .deleteAlert()
        vm.alertCount = alertData.length;

        if (vm.alertCount > 0) {
          vm.alertsNotification.hasData = true;
        } else {
          vm.alertsNotification.hasData = false;
        }

        vm.alerts = alertData;

        vm.alertsNotification.isLoading = false;
        vm.alertsNotification.hasError = false;
      }, function() {
        vm.alertsNotification.hasError = true;
      });
    };

    // Init
    (function() {
      userService.getCurrentUser().then(function(data) {
        vm.myLivestockNotification.hasError = false;
        vm.myLivestockNotification.isLoading = false;

        vm.user = data;

        // Get alert count for the user
        vm.getAlerts(vm.user.id);
      }, function() {
        vm.myLivestockNotification.hasError = true;
      });
    })();
  }

  function userMock($q, $timeout, $log) {
    var _alerts = {
        data: [{
          id: 1,
          message: "He doesn't sleep, he waits..."
        }, {
          id: 2,
          message: "He doesn't mow his lawn, he stands outside and dares it to grow."
        }, {
          id: 3,
          message: "Some magicians can walk on water. He can swim through land."
        }]
      },
      _currentUser = {
        id: 'Q2h1Y2sgTm9ycmlz'
      };

    return {
      getCurrentUser: function getCurrentUser() {
        $log.log("getCurrentUser");
        //return $q.when(_currentUser);
        return $timeout(function() { //use $timeout to simulate some REST API latency...
          return _currentUser;
        }, 500);
      },
      getAlerts: function getAlerts(id) {
        $log.log("getAlerts: " + id); //not doing anything with the id in this mock...
        $log.log(_alerts.data);
        //return $q.when(_alerts);
        return $timeout(function() {
          return _alerts;
        }, 500);
      },
      deleteAlert: function deleteAlert(userId, id) {
        $log.log("deleteAlert: " + userId + " :: " + id);

        //return $q.when(_alerts);
        return $timeout(function() {

          for (var i = 0; i < _alerts.data.length; i++) {
            if (_alerts.data[i].id === id) {
              _alerts.data.splice(i, 1);
              $log.log("alert found and deleted");
              break;
            }
          }

          $log.log(_alerts.data);

          return _alerts;
        }, 500);
      }
    };
  }

  function userService($q, $timeout, $log, userMock) {

    var service = this;

    service.getCurrentUser = userMock.getCurrentUser;

    service.getAlerts = function(id) {
      var deferred = $q.defer();

      userMock.getAlerts(id).then(function(response) {
        if (response.hasOwnProperty('data')) {

          // Convert 'he' to 'Chuck Norris'
          angular.forEach(response.data, function(alert) {
            if (alert.message) {
              alert.message = alert.message.replace(/he/gi, "Chuck Norris");
            }
          });

          deferred.resolve(response.data);
        } else {
          deferred.reject('DATA ERROR');
        }
      }, function(e) {
        deferred.reject(e);
      });

      return deferred.promise;
    };

    service.deleteAlert = function(userId, id) {
      var deferred = $q.defer();

      userMock.deleteAlert(userId, id).then(function(response) {
        deferred.resolve(response.data);
      }, function(e) {
        deferred.reject('DATA ERROR');
      });

      return deferred.promise;
    };

    return service;
  };


  angular
    .module('abp', [])
    .service('userMock', userMock)
    .service('userService', userService)
    .controller('MyLivestockController', MyLivestockController);

})();
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.28/angular.min.js"></script>
<div ng-app="abp">

  <div data-ng-controller="MyLivestockController as vm">
    <div>Alerts</div>
    <span data-ng-bind="vm.alertsNotification.isLoading ? 'Updating...' : vm.alertCount"></span>
    <div data-ng-repeat="alert in vm.alerts">
      {{alert.id}}: {{alert.message}}
      <button ng-click="vm.deleteAlert(alert.id)">Delete</button>
    </div>
  </div>

</div>