Angular ng-init $ scope函数没有正确更新模型?

时间:2015-12-06 05:44:21

标签: javascript angularjs

我现在正在构建一个Web应用程序作为测试。现在主要功能是登录和发布消息。

当用户按下按钮时,将执行以下代码:

$scope.getData = function(){
    $http.get('/messages').
        then(function(response) {
            if(angular.toJson(response.data.messages) != angular.toJson($scope.messages)) {
                $scope.messages = response.data.messages;
                $scope.clearAllSelected();
                $scope.confirmRestOperation(response);
            }
        }, function(response) {
            $scope.messages = [];
            $scope.confirmRestOperation(response);
        });
};

依赖于我的模型的HTML模板如下:

<div id="messages">
<md-card ng-repeat="message in messages" class="padding-medium box-sizing" ng-init="setAuthorDataFromAuthorId(message.author)">
  <md-checkbox class="md-primary" ng-click="toggle(message.id, selected)" ng-model="exists(message.id, selected)"></md-checkbox>
  <h4><b>Author: {{authorData[message.author].name}} ({{authorData[message.author].username}})</b></h4>
  <img style="width:50px;height:50px;" ng-src="{{authorData[message.author].profile_picture}}" alt="(Picture)">
  <p>Message: {{message.message}}</p>
  <p>{{message.time | timestampToDate}}</p>
</md-card>
</div>

所以这里的目标是,消息对象将包含author字段,其中包含作者的ID。在每个卡初始化时,我调用一个名为$scope的{​​{1}}函数,它接受一个整数值,即撰写帖子的作者的ID。以下代码是这样的:

setAuthorDataFromAuthorId

在这里看到我的目标是说屏幕上有50条消息(在任何生产场景中显然都不能加载数据库中的每条消息,但对于测试站点来说这是它正在做的事情),I如果相同的消息来自同一作者,则不希望它访问api端点$scope.setAuthorDataFromAuthorId = function(id) { // line 22 console.log($scope.authorData); // line 23 if(id in $scope.authorData) return; console.log("setAuthorFromAuthorId has been called"); $http.get('/getUserDataFromId/'+id).then(function(response) { console.log(response.data); // line 27 console.log($scope.authorData); // line 28 $scope.authorData[id] = response.data; }, function(response) { console.log(response.data); console.log($scope.authorData); $scope.authorData = {}; }); console.log("setAuthorFromAuthorId has been completed"); }; 50次。我的目标是查看id是否已经是/getUserDataFromId/1对象中的键。如果是这样,我们返回一个保存到端点的行程并加载和写入信息。因此,在一个完美的世界中,所有50条消息都会加载,而在控制台中我们只会看到这一点:

$scope.authorData

这会发生,因为它会加载作者信息一次,剩下的49会发现id是Object {} setAuthorFromAuthorId has been called setAuthorFromAuthorId has been completed Object {id: author data here} 对象中的一个键,它不会超过上面JS中的第一行功能。不过这是结果:

authorData

我根本不理解这个输出。就好像Object {} MessageController.js:23 setAuthorFromAuthorId has been called MessageController.js:25 setAuthorFromAuthorId has been completed MessageController.js:35 Object {} MessageController.js:23 setAuthorFromAuthorId has been called MessageController.js:25 setAuthorFromAuthorId has been completed MessageController.js:35 Object {full author data here from api response} MessageController.js:27 Object {} MessageController.js:28 Object {full author data here from api response} MessageController.js:27 Object {1: Object} (correct $scope.authorData) MessageController.js:28 只调用函数的几行,并将其余部分推迟到实际工作并更新模型。我做了一些研究并尝试在某些关键代码行之后添加一些ng-init$scope.$watch()以及摘要,以尝试让模型正确更新但它仍在运行第一个console.log在条件之前和消息一样多次,然后它跳过那个部分,并且运行所有$scope.$apply()的次数与消息一样多,并且破坏了我想要避免到达API端点太多无用的时间。

经过一番思考后,我决定将$http.get()更改为ng-init,看看这是否有效。它是完美的。如果有来自同一个用户的50条消息并且我点击了一条消息,那么它们都会更新(因为模型已正确更新),如果我再点击一次,则不再能够访问API,因为该功能正在运行它在词汇上应该起作用。将ng-click更改为ng-init后,输出结果如下:

ng-click

有关如何使用Object {} MessageController.js:23 setAuthorFromAuthorId has been called MessageController.js:25 setAuthorFromAuthorId has been completed MessageController.js:35 Object {full author data from API} MessageController.js:27 Object {} MessageController.js:28 // this is the authorData before it was updated with the above data Object {1: Object} MessageController.js:23 Object {1: Object} MessageController.js:23 Object {1: Object} MessageController.js:23 Object {1: Object} MessageController.js:23 // this would happen as many times as I clicked, which is the correct response 或至少某些理由说明为什么会失败ng-init,我是如何才能使这个工作正常并且符合预期(至少对我来说)?

我的直觉告诉我也许我会在写入之前尝试读取或写入一些值,这可以保证ng-init但是我已经尝试过了(虽然可能不正确!)。

由于

1 个答案:

答案 0 :(得分:2)

忘记ng-init,在控制器内移动初始化。

来自第一个$http请求的

当用户按下按钮时,请执行以下代码:

$scope.getDataAndInitPromise = function() {
  $scope.pendingFlags = $scope.pendingFlags || {};
  var promises = [];

  //compute getDataPromise
  var getDataPromise = $scope.getDataPromise();

  //chain from getDataPromise
  var initListPromise = getDataPromise.then (function() {
    angular.forEach($scope.messages, function(m) { 
       var id = m.author;
       if ($scope.pendingFlags[id] == "pending") return;
       if(id in $scope.authorData) return;
       console.log("setAuthorFromAuthorId has been called");
       //set pending flag
       $scope.pendingFlags[id] = "pending";
       var p = $http.get('/getUserDataFromId/'+id
                ).then(function(response) {
                    console.log($scope.authorData);
                    $scope.authorData[id] = response.data;
               }).catch(function(error) {
                    //log error     
               }).finally ( function() {
                    console.log("setAuthorId %s done ", id);
                    //clear pending flag
                    $scope.pendingFlags[id] = "done";
               });
        //push out promises
        promises.push(p);
    });
    //return promises array for further chaining
    return promises;
  });
  //return for further chaining
  return initListPromise;
}

请注意,如果提取已在进行中,则使用pendingFlags来阻止重复提取。另请注意,我已将“完成”的console.log报告移至.finally方法。

当然修改,getData以返回链接的承诺。

$scope.getDataPromise = function(){
  var  getDataPromise = 
    $http.get('/messages').
        then(function(response) {
            if(angular.toJson(response.data.messages) != angular.toJson($scope.messages)) {
                $scope.messages = response.data.messages;
                $scope.clearAllSelected();
                $scope.confirmRestOperation(response);
            }
        }, function(response) {
            $scope.messages = [];
            $scope.confirmRestOperation(response);
        });
  //return promise for chaining 
  return getDataPromise;
};

充分利用承诺的力量。

不要扔掉那些httpPromises;使用它们。