【问题标题】:Passing object between controllers in Angularjs在Angularjs中的控制器之间传递对象
【发布时间】:2026-01-15 00:20:05
【问题描述】:

我是 Angular 新手,正在努力了解如何使用服务将数据从一个控制器传递到另一个控制器。

到目前为止,使用 Angular,我已经能够理解如何调用控制器并将数据从 HTML 中传递给它们。为了设置地图控制器,这特别好,我查看来自 PHP 的结果列表,并将这些结果中的数据发送到我的控制器,以在地图上创建标记。

让我完全困惑的是,有一个控制器通过 ElasticSearch 调用生成嵌套对象,然后将该数据传递给我的地图控制器。

最初我尝试通过 ng-repeat 循环遍历结果,虽然我能够输出结果,但我的 MapController 无法读取 HTML 中的这些结果,因为它们在我的 IndexController 的范围内(这是输出数据)。好吧,我想是这样的。

这是我尝试过的,当它输出数据时,我无法在 MapController 中读取它。

<div ng-controller="IndexController">
    <div ng-repeat="r in listingsArea.results">
        <div class="component home-listings"
            data-id="{{r.id}}"
            data-url="{{r.url}}"
            data-lat="{{r.lat}}"
            data-lng="{{r.lon}}"
            data-address="{{r.address}}"
            data-price="{{r.price}}"
        ></div>
    </div>
</div>

我了解到,将数据从一个控制器传递到另一个控制器的最佳方式是通过服务。我在这里关注了很多文档,但尽管如此,我显然遗漏了一些东西,因为它不起作用。

这是我目前所拥有的:

ResultsService.js

App.factory("resultsService", function() {

var loadListingsArea = {};

return {
    getLocations: function() {
        return loadListingsArea
    },

    setLocations: function(loc) {
        loadListingsArea = loc;

IndexController.js

App.controller('IndexController', function($scope, resultsService) {

$scope.listingsArea = [];

$scope.loadListingsArea = function() {
    $http.get($window.location + '/listingsarea')
        .success(function(data) {
            $scope.listingsArea = data;
        }
    );
}

$scope.$watch(function() {
    return $scope.loadListingsArea;
}, function() {
    resultsService.setLocations($scope.loadListingsArea);
});
});

MapController.js(只是在这个阶段尝试转储对象,不包括我很长的谷歌地图代码库)

App.controller('MapController', function($scope, resultsService) {

$scope.locations = resultsService.getLocations();

alert($scope.locations);

这是我的对象从 indexcontroller.js 转储时的样例

{"count":4,
"results":[
      {"id":"1153292",
       "url":"/this-is-my-slug/1153292",
       "lat":"-37.822034",
       "lon":"144.969553",
       "address":"1302/430 Place Road",
       "price":"$2,350,000",
       "hero":"some-image-here.jpg"}
]};

【问题讨论】:

  • 使用服务分享,或者使用$broadcast直接发送
  • 哦,我明白了,我不知道广播。所以你可以直接在一个控制器中启动它以发送到另一个控制器?

标签: javascript angularjs angular-controller


【解决方案1】:

$http 服务返回一个承诺。存储那个。不需要做$q.defer 的东西。

App.factory("resultsService", function() {

  var self = this;
  self.listingsPromise = undefined;

  return {
      loadLocations: function(url) {
           self.listingPromise = $http.get(url);
           return self.listingPromise;
      },
      getLocations: function() {
        return self.listingPromise;
      }
  );
}

发起检索的控制器可以这样做:

resultsService.loadLocations() // returns a promise
  .then(function(response){
    $scope.listingsArea = response.data;
  }) .catch ( function (error) {
    throw error;
  });

而只需要获取它的控制器就可以这样做:

resultsService.getLocations()
  .then(function(response){
    $scope.listingsArea = response.data;
  }) .catch ( function(error) {
    throw error;
  });

请注意,两个控制器都需要检查错误。

还要注意.then 返回的数据与.success 不同。

AngularJS 团队已经意识到并弃用了.success。从现在开始,我们都应该使用.then

有关弃用.success 的信息以及更多信息,请参阅最新的AngularJS $http API Docs

【讨论】:

  • 经过测试并具有魅力。从它的运行方式来看,从我对脚本的这些元素的阅读来看,等待不同的部分运行,然后再进入下一个部分。
  • 如果在 loadLocations 之前调用 getLocations 将返回一个空对象,并且调用 .then 将失败。因此,当包含第二个控制器但未包含第一个控制器时,这将失败。无论哪种方式,我都会返回一个承诺,并且 .then 不会失败,但如果 loadLocations 从未被调用,也不会被解决。
  • 不完全适用,因为这里需要从第二个控制器访问数据。为了增加安全性,您必须在 getLocations 中检查 self.listingPromise 实际上是一个承诺,如果不是,您将不得不返回一个。您可以将方法更改为延迟加载,因此如果承诺尚不存在,您可以调用 load 函数并获取承诺。或者从两个控制器调用 load 方法,如果 promise 已经存在,则返回它而不是发出新请求。但照原样,您的代码会导致未处理的错误。
  • 这仍然会返回空对象,因为空对象是真实的。尝试做一个 typeof,比如 if (self.listingPromise && typeof self.listingPromise.then === 'function') return self.listingPromise。或使用 null 而不是 {} 初始化 ListingPromise
【解决方案2】:

将 $http.get 移动到服务中的函数中。 编辑为使用 $q.deferred 而不是返回 $http 的承诺。

App.factory("resultsService", function() {

  var self = this;
  self.deferred = $q.defer();
  self.listingsArea = {};

  return {
      loadLocations: function() {
          $http.get($window.location + '/listingsarea')
              .then(function(data){
                 self.listingsArea = data;
                 self.deferred.resolve(self.listingsArea);
              },
              function(e){
                self.deferred.reject(e);
              });
          return self.deferred.promise;
      },
      getLocations: function() {
        return self.deferred.promise;
      }
  );
}

发起检索的控制器可以这样做:

resultsService.loadLocations() // returns a promise
  .then(function(data){
    $scope.listingsArea = data;
  });

而只需要获取它的控制器就可以这样做:

resultsService.getLocations()
  .then(function(data){
    $scope.listingsArea = data;
  });

这可能需要一些额外的按摩,但它应该会给你一个前进的方向。

另请注意,以这种方式共享对象意味着您可以从任一控制器编辑相同的对象。为避免这种情况,请改为传递数据对象的 angular.copy。

【讨论】:

  • 嗯,在此过程中出现了一些问题,因为通过使用您的建议,地图控制器永远不会完全加载,因此不会执行。问题似乎出在索引控制器中,因为注释掉那里的代码然后确保它执行。
  • 找到了,只好在函数中定义$http和$window。尽管如此,仍然不能 100% 确定整个对象是否被共享。
  • 我在我的应用程序中做了类似的事情。我有错误回调,并且我没有将数据放在 $scope 上,因为我使用“controller as”语法,这是我能想到的唯一区别。这应该以您需要的方式共享对象。
  • 很公平。经过进一步检查,我已经能够确认该服务正在检索对象,该对象正在传递给 indexController,但它没有传递给 mapController。此时,getLocations 始终返回 null。
  • 尝试将 listingArea var 放在 self 的服务中,请参阅我的编辑。如果不这样做,它可能是一个竞争条件。您是同时实例化两个控制器,还是一个替换另一个(就像您使用 ngView 一样)?如果您调用加载,然后在返回任何数据之前调用 get,则第二个控制器将变为空。为了解决这个问题,您可以将数据放在包装器对象中并存储/共享包装器对象。这样,一旦数据返回并放入包装器对象,它将更新两个控制器
最近更新 更多