Javascript Angular ng init$scope函数未正确更新模型?

Javascript Angular ng init$scope函数未正确更新模型?,javascript,angularjs,Javascript,Angularjs,我现在正在构建一个web应用程序作为测试。现在的主要功能是登录和发布消息 当用户按下按钮时,执行以下代码: $scope.getData = function(){ $http.get('/messages'). then(function(response) { if(angular.toJson(response.data.messages) != angular.toJson($scope.messages)) {

我现在正在构建一个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>
这里我的目标是假设屏幕上有50条消息(显然,在任何生产场景中加载数据库中的每条消息都不现实,但对于测试站点,这就是它所做的),如果同一消息来自同一作者,我不希望它访问api端点
/getUserDataFromId/1
50次。我的目标是查看id是否已经是
$scope.authorData
对象中的一个键。如果是这样的话,我们将返回一个保存自己到端点并加载和写入信息的过程。因此,在一个完美的世界中,所有50条消息都将加载,在控制台中,我们只能看到以下内容:

Object {}
setAuthorFromAuthorId has been called
setAuthorFromAuthorId has been completed
Object {id: author data here}
这是因为它会加载一次作者信息,而剩下的49次则会看到id是
authorData
对象中的一个键,并且不会超过上述JS函数中的第一行。然而,结果如下:

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
我完全不理解这个输出。这就好像
nginit
只调用函数的两行,而将其余部分推迟到以后实际工作和更新模型。我做了一些研究,尝试添加了一些
$scope.$watch()
's和
$scope.$apply()
's,并在某些关键代码行之后进行消化,以尝试正确更新模型,但它会在条件之前多次运行第一个console.log,然后跳过该部分,运行所有的
$http.get()
的次数与消息的次数一样多,这破坏了我不想让自己多次到达API端点的愿望

经过一些思考,我决定将
ng init
更改为
ng click
,看看这是否有效。太完美了。如果有50条消息都来自同一个用户,而我点击了其中一条,那么它们都会更新(因为模型已经正确更新),如果我点击了更多次,那么API将无法访问,因为函数正在按照其词汇应该如何工作。这是将
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
失败的原因提出一些建议

我的直觉告诉我,也许我可以在写某个值之前读或写它,这将保证有一个
$scope.$watch()
,但我已经尝试过了(也许没有正确!)


谢谢

忘记
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
,以防止在提取过程中重复提取。还请注意,我将“done”的
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;
};
利用承诺的力量。


不要抛弃那些承诺;使用它们。

使用
nginit
可能无法做到这一点。
ng init
的文档非常清楚,它仅适用于少数特定情况,并且在这些特殊情况之外,它可能会正确执行,也可能不会正确执行,因为它是具有优先级的指令。其他具有更高优先级的指令可能会更早执行,期望得到尚未处理的结果。好的,您知道我可以在这里使用什么作为
ng init
替代方案吗?我做了一些研究,似乎没有太多东西可以替代它来加载值。因此,您正在使用异步的
$http
,然后尝试根据第一个异步调用的结果进行另一个
$http
异步调用。本质上,即使第一个和第二个
消息
具有相同的
作者
,第二个消息也会在第一个消息返回数据之前查找相同的
作者
,从而导致它对相同的数据进行另一次调用。这是切换到
$resource
的理想情况,它可以自动缓存服务器请求。您也可以使用
$cacheFactory
为特定请求创建自己的缓存,但要完美实现这一点可能有点困难。在这种情况下,实现起来可能很容易,因为我要执行的请求非常简单,而且在这个控制器中,我只会使用$resource,然而,我的问题是:如果一些用户数据得到异步更改,并且缓存的
/getUserDataFromInfo
从技术上给了我错误的值,该怎么办。在这个例子中,这是一个相当大的范围,因为没有什么东西会改变这么快(加载50条消息所需的时间左右),但原理是一样的,所以一般来说,我如何处理它,或者这是一个不能处理的东西,因此缓存?
$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;
};