【问题标题】:Update $scope when $resource promise resolves当 $resource 承诺解决时更新 $scope
【发布时间】:2014-11-16 11:41:57
【问题描述】:

我一直在努力思考如何正确使用 angular $scope。我的应用在加载主页时根据从服务器接收到的 json 数据中的 url 链接更新视图。

控制器在单击按钮时获取下一个数据文件,但我无法将此数据注入我的范围变量并更新视图。网络成功传输数据,因此我的服务正在运行。

我不明白如何让 $scope 解决 $resource 承诺并在应用程序初始化后检索数据时更新视图。非常感谢您对此事的任何帮助。

我的 ListController :

//View updates correctly when promise resolves
$scope.data = DataElements.get({}); 

$scope.nextPage = function (){ //Called when clicking 'next' button
var url = $scope.data.pager.nextPage; 

var pageNumber = url.split("page=").pop(); //Ugly but it is temporary, I promise..

//View doesnt update
DataElements.get({'page': pageNumber}, function(data){ 
    $scope.data = data; 
});

//Error: Digest already in progress
DataElements.get({'page': pageNumber }, function(data){ 
    $scope.$apply(function(){
      $scope.data = data; 
    });
});

//View doesnt update
$scope.data = DataElements.get({'page': pageNumber)};  

};

服务:

services.factory('DataElements', ['$rootScope','$resource', function($rootScope, $resource){
    return $resource($rootScope.baseUrl+'api/:endPointAdr.jsonp', 
        {'endPointAdr': 'dataElements', 'page': '@page'}, 
        { get : {'method' : 'JSONP', 'params' : {'callback' : 'JSON_CALLBACK'}}     
    });
}]);

查看:

<section>
  <div>
    <h1>Search</h1>
    <input type="text" placeholder="Type something" autofocus>
    <input type="button" name="filter" value="Filter">
    <input type="button" name="clear" value="Clear">
    <div class="btn-toolbar" role="toolbar" aria-label="...">
      <a class="btn btn-default" href="#" aria-label="Previous Page">
        <span class="glyphicon glyphicon-chevron-left"></span>
      </a>
      <a class="btn btn-default" href="#" ng-click="nextPage()" aria-label="Next Page">
        <span class="glyphicon glyphicon-chevron-right"></span>
      </a>
    </div>
  </div>
  <div ng-controller="ListController">
    <ul class="" ng-show="data">
      <li class="" ng-repeat="element in data.dataElements ">
        <a href="#">
          <div class="info">
            <h2>{{element.name}}</h2>
          </div>
        </a>
      </li>
    </ul>
  </div>
</section>

服务器响应:

DataElements.get({}) == >
angular.callbacks._1({"pager": {"page":1,"pageCount":15,"total":706,"nextPage":"http// ...});(*)

DataElements.get({'page': 2})  == > 
angular.callbacks._2({"pager": {"page":2,"pageCount":15,"total":706,"nextPage":"http:// ...});(*)

(*) $resource 去掉 jsonp 并且只返回 json。

更新:

如果我只在nextPage 函数范围内分配$scope.data,它就可以完美地工作。我知道ng-clickdirective 将此函数包装在$scope.apply() 中。然后,显式调用 apply 会导致错误是有道理的。

解决方案:

&lt;div ng-controller="ListController"&gt; 创建了一个具有不同 $scope 的重复控制器。删除它解决了问题。详情见我的回答。

【问题讨论】:

  • 不确定是否为jsonp 配置了$resource。你试过$http.jsonp() 吗?服务器实际上是在传递jsonp 吗?
  • 我没试过,但是第一个请求成功返回jsonp。
  • 第二个(分页)调用是否返回数据?你怎么看?
  • 我已经更新了帖子。第二页返回具有相同结构的数据,除了一个额外的 pagePrevious 属性。

标签: angularjs angularjs-scope angularjs-ng-click angular-promise angularjs-view


【解决方案1】:

如果DataElements.get返回一个promise对象,你需要调用then方法:

DataElements.get({'page': pageNumber}).then(function(data) {
   $scope.data = data;
})

或者如果它使用 $resource 通常会返回一个带有$promise 属性的对象:

DataElements.get({'page': pageNumber}).$promise.then(function(data) {
   $scope.data = data;
})

【讨论】:

  • promise 属性存在,但调用 .then 或 $promise.then 只会更新 $scope 上的 data 属性,视图更新仍然不会触发。
  • 我在帖子中没有说清楚,抱歉。 $scope.data 在视图中生成一个列表。此列表应在 $scope.data 更改时更新。
  • $resource对象,不用$promise.then调用,像OP一样传入成功回调即可。
【解决方案2】:

我终于解决了这个问题。

归结为&lt;div ng-controller="ListController"&gt;directive。它创建了第二个ListController 并将其附加到$rootScope。绑定没有问题,但 nextPage() 是从不同的范围调用的。

在动态 ListController 的初始化阶段,两个控制器都将执行 DataElements.get..,这就是它看起来工作的原因,但调用 ng-click : nextPage() 并没有在分配给 list.htmlview 的控制器的上下文中运行.

【讨论】:

  • 这是有道理的,但是在提供的 HTML 示例中看到 ListController 的二次使用会很好。
【解决方案3】:

正如您所提到的,您在 $scope 上的数据正在更新,但更改不会传播到视图。有东西打破了双向绑定。

查看您在控制台中遇到的错误类型(Chrome 中的F12)。

删除此代码块:

//Error: Digest already in progress
DataElements.get({'page': pageNumber }, function(data){ 
    $scope.$apply(function(){
      $scope.data = data; 
    });
});

//View doesnt update
$scope.data = DataElements.get({'page': pageNumber)};  

避免明确使用$apply,这不是一个好主意。

编辑:

虽然 OP 通过删除与 ListController 的绑定以避免 $scope 混淆来解决他的问题,但 HTML 代码现在看起来有点奇怪。最佳解决方案是创建一个绑定到ListController 的指令,然后在视图中重用该指令。这样,OP 将在未来避免此类$scope 问题。

【讨论】:

  • $apply 产生:错误:[$rootScope:inprog] $digest 已经在进行中。我试图强制 Angular 评估其 watchExpressions 并检测数据属性已更改。我只在测试期间使用 $apply 作为衡量标准。应用产生错误,而其他错误则静默失败。
  • 是的,您收到此错误是因为 Angular 已经忙于(正在进行)消化。那时,您无法强制更新视图。看看是什么破坏了您的绑定,控制台中有任何指示吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-03-15
  • 1970-01-01
  • 2017-05-09
  • 1970-01-01
  • 2015-06-24
  • 2018-11-20
  • 2022-06-14
相关资源
最近更新 更多