我正在尝试从ng-repeat输入获得$ http调用的响应。然后确定是否在每个响应中显示单独的ng-repeat。目前实际的$ http api不可用,所以我刚刚对响应进行了硬编码。
没有使用承诺就可以正常工作。但是在使用promise方法时,我甚至无法在jsfiddle上运行它。 我怎样才能使承诺有效?
编辑:感谢Benjamin Gruenbaum,我已经重新回答了我的问题 我实际上并没有使用$ http,而是使用pouchDB(本地数据库,因此无需担心DOS)。仅使用$ http作为示例,因为它也返回一个promise。 我真正需要解决的是如何在添加新名称时再次更新承诺。例如,如果添加“joe”,那么他应该在'billsNotPaid'
上http://jsfiddle.net/TheSlamminSalmon/cs0gxxxd/6/
查看:
<div ng-app>
<div ng-controller="MainController">
<h1>Hello Plunker!</h1>
<form ng-submit="AddNewGuy()">
<input type="text" ng-model="newGuy" placeholder="Insert New Guy"></input>
<input type="submit"></input>
</form>
<strong>Everyone</strong>
<div ng-repeat="name in names">
{{name}}
</div>
<strong>These guys haven't paid their bills (Non Promise)</strong>
<div ng-repeat="name in names">
<div ng-show="NotPaidBillsNonPromise(name)">
{{name}}
</div>
</div>
<strong>These guys haven't paid their bills (Using http Promise)</strong>
<div ng-repeat="name in billsNotPaid">
{{name}}
</div>
</div>
</div>
AngularJS控制器:
function MainController($scope, $http) {
$scope.names = [
"James",
"Tim",
"Alex",
"Sam",
"Kim"
];
$scope.billsNotPaid = []; // start as empty
$scope.NotPaidBillsNonPromise = function (name) {
if (name == "Tim" || name == "Sam" || name == "Joe") return true;
};
$scope.NotPaidBills = function (name) {
return $http.get("http://echo.jsontest.com/name/" + name)
.then(function (r) {
return (r.data.name === "Tim" || r.data.name === "Sam" || r.data.name === "Joe")
});
};
$scope.newGuy;
$scope.AddNewGuy = function () {
$scope.names.push($scope.newGuy);
};
// start the check for each name
$scope.names.forEach(function(name){
return $scope.NotPaidBills(name).then(function(notPaid){
console.log(name, notPaid);
if(notPaid) $scope.billsNotPaid.push(name);
});
});
}
答案 0 :(得分:2)
首先 - 我强烈建议您避免在ng-repeat中为每个项目发出HTTP请求。它很慢并且很可能会导致糟糕的用户体验 - 相反,我建议您将这些请求批量处理为一个请求,该请求需要多个值并返回多个值。
Angular通过双向数据绑定执行更新,绑定值通过称为摘要周期的循环更新 - 因此每当Angular运行这样一个循环时,所有值都保证最新到循环运行的时间。 / p>
幸运的是 - 由于$http
会返回一个承诺 - Angular会在通话结束后安排摘要,因为承诺会通过then
运行$evalAsync
个回调,这意味着如果将会安排摘要一个尚未安排或正在进行中。
因此,您可以从$http
承诺履行处理程序更新范围,并且它可以正常工作。我们添加了一个新的范围属性:
$scope.billsNotPaid = [];
这里是相应的Angular:
<div ng-repeat="name in billsNotPaid">
{{name}}
</div>
电话:
$scope.NotPaidBills = function (name) {
return $http.get("http://echo.jsontest.com/name/" + name)
.then(function (r) {
return (r.data.name === "Tim" || r.data.name === "Sam")
});
};
// start the check for each name
$scope.names.forEach(function(name){
return $scope.NotPaidBills(name).then(function(notPaid){
console.log(name, notPaid);
if(notPaid) $scope.billsNotPaid.push(name);
});
});
答案 1 :(得分:1)
没有什么新的,只是总结了其他答案中所说的内容。
如果您正在越过网络边界,请不要使用繁琐的界面,尤其是当它在像ng-repeat这样的循环中时。因此,您应该加载任何必要的内容以提前呈现视图(即视图被激活时),并且对于需要添加到视图的每个新项目,您可以将其直接添加到添加处理程序中的UI并使用服务将它保留回数据存储(如果跨越网络边界/ api是基于承诺的话,很可能是基于异步声明的呼叫)。
更新了Benjamin的评论... 除了 $ http ,当使用jsfiddle或类似的东西时,你绝对可以模拟穿越网络边界使用有角度的 $ q 或 $ timeout 服务。简而言之,通过调用var dfd = $q.defer();
创建延迟对象,使用dfd.resolve
解析承诺,最后使用return dfd.promise
返回承诺,或使用 $ timeout 并在超时函数中返回结果,如下所示:
-
function asyncSomething() {
var dfd = $q.defer();
var result = 1 + 1; //do something async
dfd.resolve(result);
return dfd.promise;
}
或
function asyncSomething() {
return $timeout(function() {
return 1 + 1; ///do something async
}, 1000); //pretend async call that last 1 second...
}
以约翰帕帕的风格改变了你的小调,作为上述内容的一个例子:http://jsfiddle.net/kx4gvsc0/30/
在上面的例子中,我试图以不同的方式解决问题。我没有循环遍历列表中的所有名称,而是将清理欠款列表的责任移交给addCustomer方法(是的,不是一个好主意,你可以将它分成2个更符合SRP的调用,但对于在任何情况下,作为异步添加客户调用的一部分,它将链接另一个承诺(可能是pouchdb调用,也是承诺基础),它将刷新欠款列表并返回回到控制器进行绑定。
function addCustomerAndRefreshOwingList(customerName) {
return $timeout(function() {
//persist the new customer to the data source
_customers.push({ name: customerName, paid: false});
}, 250) //pretend network request lasting 1/4 second
.then(refreshOwingList); //chain another promise to get the owing list <-- This is your pouch db call perhaps...
}
function refreshOwingList() {
return $timeout(function() {
//Fake logic here to update certain payers per call using the counter
switch(counter) {
case 0: // first pass...
_customers[1].paid = true; //Tim paid
break;
case 1: // second pass...
_customers[2].paid = true; //Alex paid
_customers[4].paid = true; //Kim paid
break;
default:
break;
}
counter++;
//return all customers that owes something
return _customers
.filter(function(c) { return !c.paid; })
.map(function(c) { return c.name; });
}, 500); //pretend this is taking another 1/2 second
}