【问题标题】:AngularJS - ng-bind not updatingAngularJS - ng-bind 不更新
【发布时间】:2015-05-08 08:46:36
【问题描述】:

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

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

前端

<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>

控制器

(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);

})();

服务

(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);

})();

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

【问题讨论】:

  • 你试过 ng-model
  • 将其更改为 ng-model = "vm.alertCount" 应该可以工作
  • @PaulFitzgerald。 ng-model 不能与 span 一起使用。
  • @Sourabh- 很有趣,我不知道。谢谢
  • 看看我的回答,别再让这家伙糊涂了。他绑定的值是根据服务的承诺更新的,因此在摘要之外。 $scope.apply() 应该可以解决问题。

标签: javascript angularjs


【解决方案1】:

您的绑定没有更新,因为您在 Angular 应用的摘要周期之外更改了 alertCount 的值。当您刷新应用程序时,摘要会运行,因此您的值会更新。像这样在 $scope.apply() 中包装变量的更新:

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

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

$scope.$apply();

希望对您有所帮助。

编辑 -----
鉴于您使用完整代码进行了更新,我发现您没有在控制器中的任何地方注入作用域,我编写的控制器通常是这样开始的:

(function () {

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

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

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

编辑 -----
这是我对您的代码的快速了解:

(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;
        });
    })();
}]);

})();

大体思路是:

  1. 您创建了一个应用程序 (angular.module)
  2. 您在此应用中创建一个控制器,并注入 $scope
  3. 您希望在视图中更新的任何值,都添加到 $scope
  4. 如果您在回调、事件或承诺中有任何 $scope 更新,请将它们包装在(或跟随)$scope.$apply 调用中

我认为这应该适合你:)

【讨论】:

  • 谢谢,丹尼尔。但是,我正在努力申请$scope.apply(),因为这样做时我遇到了控制台错误。我已经更新了我的原始代码以提供清晰和完整的控制器,我认为我的 $scope is not defined 错误是因为我将范围引用为 vm 是对的吗?
  • 我用适合你的代码编辑了我的答案。
【解决方案2】:

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

这似乎有效,是吗?

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

更新:当您使用userService 代码更新您的问题时,我已经更新了以下内容以更加匹配。我仍然有一个模拟服务代替userServiceuser 依赖项。此外,我对 Controller 类进行了一些小修改,以便在 Promise 仍在解决时,我们可以看到“正在更新...”来代替警报计数。

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

(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>

【讨论】:

  • 嗨@JcT,谢谢你的回复,我已经用用户服务更新了原帖。
  • 谢谢,@AlexRyans,很高兴为您提供帮助 - 我也在上面进行了快速更新,但仍在尝试归零;如果我能弄清楚如何重现问题,希望能更好地找到问题的根源!
  • 我认为下一步将尝试提出在 sn-p 或 plunkr 中重现问题的最简单的代码集;有意义吗?
猜你喜欢
  • 2018-06-05
  • 2015-04-10
  • 1970-01-01
  • 1970-01-01
  • 2016-10-17
  • 1970-01-01
  • 2013-09-26
  • 2023-03-15
  • 1970-01-01
相关资源
最近更新 更多