你如何在AngularJS Views(ng-show)中使用promises?

时间:2015-04-04 10:22:55

标签: javascript angularjs promise angular-promise

我正在尝试从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); 
        });
    });
}

2 个答案:

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

Fiddle

答案 1 :(得分:1)

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

  1. 如果您正在越过网络边界,请不要使用繁琐的界面,尤其是当它在像ng-repeat这样的循环中时。因此,您应该加载任何必要的内容以提前呈现视图(即视图被激活时),并且对于需要添加到视图的每个新项目,您可以将其直接添加到添加处理程序中的UI并使用服务将它保留回数据存储(如果跨越网络边界/ api是基于承诺的话,很可能是基于异步声明的呼叫)。

  2. 更新了Benjamin的评论... 除了 $ http ,当使用jsfiddle或类似的东西时,你绝对可以模拟穿越网络边界使用有角度的 $ q $ timeout 服务。简而言之,通过调用var dfd = $q.defer();创建延迟对象,使用dfd.resolve解析承诺,最后使用return dfd.promise返回承诺,或使用 $ timeout 并在超时函数中返回结果,如下所示:

  3. -

    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's Angular style guide。这只是一种很好的做法。
    2. 以约翰帕帕的风格改变了你的小调,作为上述内容的一个例子: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        
          }