Angularjs 在$resource承诺解析时更新$scope

Angularjs 在$resource承诺解析时更新$scope,angularjs,angularjs-scope,angularjs-ng-click,angular-promise,angularjs-view,Angularjs,Angularjs Scope,Angularjs Ng Click,Angular Promise,Angularjs View,我一直在想如何正确使用angular$scope。我的应用程序根据加载主页时从服务器收到的json数据中的url链接更新视图 控制器在单击按钮时获取下一个数据文件,但我无法将此数据注入我的范围变量并更新视图。网络成功传输数据,因此我的服务正常工作 我不明白在应用程序初始化后,如何让$scope在检索数据时解析$resource承诺并更新视图。非常感谢在这件事上的任何帮助 我的ListController: //View updates correctly when promise resolve

我一直在想如何正确使用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)};  

};
<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:// ...});(*)
服务:

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'}}     
    });
}]);
查看:

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

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

更新:

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

};
<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:// ...});(*)
如果我只在
nextPage
函数范围中分配
$scope.data
,那么它工作得非常好。我知道
ng click
指令将此函数包装在
$scope.apply()
中。然后显式调用apply会导致错误,这是有道理的

解决方案:

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

};
<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:// ...});(*)

创建了具有不同$scope的重复控制器。移除它修复了问题。有关详细信息,请参见我的答案。

如果
数据元素。get
返回需要调用
然后
方法的承诺对象:

DataElements.get({'page': pageNumber}).then(function(data) {
   $scope.data = data;
})
或者,如果它使用$resource,则通常返回一个对象,该对象上有
$promise
属性:

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

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

查看控制台中出现的错误类型(
F12
在Chrome中)

删除此代码块:

//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
,这很少是个好主意

编辑:

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

};
<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:// ...});(*)

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

我终于解决了这个问题

它归结为这个指令。它创建了第二个
ListController
,并将其附加到
$rootScope
。绑定没有问题,但是从不同的范围调用了
nextPage()


在dynamic ListController的初始化阶段,两个控制器都将执行
DataElements.get..
,这就是为什么它似乎可以工作,但调用
ng click:nextPage()
并没有在分配给
list.html
视图的控制器的上下文中运行

不确定是否为
jsonp
配置了
$resource
。您是否尝试过
$http.jsonp()
?服务器是否实际交付了
jsonp
?我还没有尝试,但第一个请求成功返回了jsonp。第二个(分页)调用了返回数据吗?你认为呢?我更新了帖子。第二页返回结构相同的数据,除了一个额外的pagePrevious属性。promise属性存在,但正在调用。then或$promise。然后只更新$scope上的数据属性,视图更新仍然不会触发。我在帖子中没有足够清楚地说明,抱歉。$scope.data在视图中生成一个列表。此列表应在$scope.data更改时更新。它是一个对象,无需使用
$promise调用它。然后
,像OP那样传入成功回调就可以了。$apply product:Error:[$rootScope:inprog]$摘要已在进行中。我试图强制angular计算其watchExpressions,并检测数据属性是否已更改。在测试期间,我仅使用$apply作为度量值。应用生成错误,其他错误将以静默方式失败。是的,您会收到此错误,因为Angular正在忙于(正在)消化。此时,您无法强制完成对视图的更新。查看是什么破坏了您的绑定,控制台中有任何指示吗?这是有意义的,但是如果在提供的HTML示例中看到ListController的二次使用,那将是很好的。