如何在AngularJS视图中使用promise(ng-show)

How do you use promises in AngularJS Views (ng-show)?

本文关键字:promise ng-show AngularJS 视图      更新时间:2023-09-26

我正试图从ng个重复输入中获得$http调用的响应。然后确定是否显示每个响应的单个ng重复。目前实际的$httpapi不可用,所以我只是对响应进行了硬编码。

它在不使用承诺的情况下工作得很好。但是在使用promise方法时,我甚至无法在jsfiddle上运行它。我怎样才能兑现承诺?

编辑:感谢Benjamin Gruenbaum,我重新提出了我的问题我实际上并没有使用$http,而是使用pouchDB(本地DB,所以不需要担心DOS)。只使用$http作为示例,因为它也返回了一个promise。我真正需要解决的是如何在添加新名称时再次更新承诺。例如,如果添加了"joe",那么他应该在"billsNotPayed"上

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

首先,我强烈建议您避免在ng重复中对每个项目发出HTTP请求。它很慢,可能会导致糟糕的用户体验——相反,我建议您将这些请求批处理为一个接受多个值并返回多个值的单个请求。


Angular通过双向数据绑定执行更新,绑定值通过一个称为摘要循环的循环进行更新,因此每当Angular运行这样的循环时,您的所有值都保证在循环运行时是最新的。

幸运的是,由于$http返回promise,Angular将在调用结束后安排摘要,因为promise通过$evalAsync运行其then回调,这意味着如果尚未安排或正在进行摘要,则将安排摘要。

因此,您只需从$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); 
    });
});

Fiddle

没有什么新鲜事,只是总结了其他答案中所说的内容。

  1. 如果你正在跨越网络边界,不要制作健谈的界面,尤其是当它在像ng重复这样的循环中时。因此,您应该提前加载渲染视图所需的任何内容(即,当视图被激活时),对于需要添加到视图中的每一个新项,您都可以将其直接添加到添加处理程序中的UI中,并使用服务将其持久化回数据存储(如果跨越网络边界/api是基于promise的,则很可能是一个异步的基于promise的调用)。

  2. 根据Benjamin的评论更新或者,在使用jsfiddle或类似服务时,可以使用angular$q$stimeout服务来模拟跨越网络边界。简而言之,通过调用var dfd = $q.defer();创建一个defer对象,使用dfd.resolve解析promise,最后使用return dfd.promise返回promise,或者使用$timeout包装模拟网络调用,然后在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...
}
  1. 是的,如果你还没有遵循John Papa的Angular风格指南,一定要使用它。这只是很好的练习

以下是你的小提琴,以约翰·帕帕的风格为例进行了修改:http://jsfiddle.net/kx4gvsc0/30/

在上面的例子中,我试图用不同的方式来解决这个问题。我没有循环遍历列表中的所有名称,而是将刷新欠列表的责任交给addCustomer方法(是的,这不是一个好主意,你可以将其分为两个更符合SRP的调用,但为了使调用变粗…),在任何情况下,作为异步添加客户调用的一部分,它链接另一个promise(可能是pouchdb调用,也是promise库),它将刷新欠列表并将其返回给控制器进行绑定。

    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        
    }