Javascript Angular ng init$scope函数未正确更新模型?
我现在正在构建一个web应用程序作为测试。现在的主要功能是登录和发布消息 当用户按下按钮时,执行以下代码: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)) {
$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;
};