在控制器之间共享异步数据而不会发出多个请求

kiw*_*342 9 angularjs angularjs-service angularjs-scope

我正在尝试单个$http请求获取我的一个JSON文件并使用我所有控制器中的数据.

我在egghead.io上看到了如何在多个控制器之间共享数据,我还阅读了这个StackOverflow问题:" 在angular.js中的控制器之间共享一个变量 ".

但是,那里的答案不使用该$http模块.使用时$http,控制器没有要处理的数据,并且在收到响应时已经太晚了.

然后我$q.defer在StackOverflow上找到了方法和这个问题:" AngularJS在控制器之间共享异步服务数据 "

在那里发布的解决方案工作正常,但它有两个问题:

  1. 每个控制器将触发$http请求以获得已在另一个控制器中使用的相同数据; 和,
  2. 如果我试图操纵收到的数据我有一个then功能.

您可以在下面看到我的代码:

controllers.js

'use strict';

/* Controllers */

function appInstallerListCtrl($scope, Data) {
  $scope.apps = Data;
}

function appInstallerDetailCtrl($scope, $routeParams, Data) {
  $scope.appId = $routeParams.appId;
  $scope.apps = Data;  
  console.log($scope.apps); // <-- then function
  console.log(Data); // <-- then function with $vv data returned but I can't access it

  for (var i in $scope.apps) // <--- no way, baby!
    console.log(i);
}
Run Code Online (Sandbox Code Playgroud)

app.js

var app = angular.module('appInstaller', []);

app.factory('Data', function($http, $q) {
  var defer = $q.defer();
  $http.get('apps.json').then(function(result) {
    defer.resolve(result.data.versions.version);
  });
  return defer.promise;
});

app.config(['$routeProvider', function($routeProvider) {
  $routeProvider.
    when('/app', {templateUrl: 'partials/app-list.html',   controller: appInstallerListCtrl}).
    when('/app/:appId', {templateUrl: 'partials/app-detail.html', controller: appInstallerDetailCtrl}).
    otherwise({redirectTo: '/app'});
}]);
Run Code Online (Sandbox Code Playgroud)

我想要的是,在启动应用程序时,$http将执行请求,并且将在整个应用程序中跨所有控制器使用响应.

谢谢

Mar*_*cok 14

我喜欢将我的数据存储在服务中,并向控制器返回一个承诺,因为通常你需要处理那里的任何错误.

app.factory('Data', function($http, $q) {
   var data = [],
       lastRequestFailed = true,
       promise;
   return {
      getApps: function() {
         if(!promise || lastRequestFailed) {
            // $http returns a promise, so we don't need to create one with $q
            promise = $http.get('apps.json')
            .then(function(res) {
                lastRequestFailed = false;
                data = res.data;
                return data;
            }, function(res) {
                return $q.reject(res);
            });
         }
         return promise;
      }
   }
});

.controller('appInstallerListCtrl', ['$scope','Data',
function($scope, Data) {
    Data.getApps()
    .then(function(data) {
        $scope.data = data;
    }, function(res) {
        if(res.status === 500) {
            // server error, alert user somehow
        } else { 
            // probably deal with these errors differently
        }
    });
}]);
Run Code Online (Sandbox Code Playgroud)

在解决/拒绝承诺后注册的任何回调将立即使用相同的结果/ failure_reason解决/拒绝.一旦解决/拒绝,承诺就不能改变(其状态).所以第一个调用的控制器getApps()将创建承诺.任何其他调用的控制器getApps()将立即获得返回的承诺.

  • @Anze,我明白了.如果你将if语句更改为`if(!promise)`那么你只能获得一个GET请求.似乎有两个控制器,在设置`lastRequestFailed`之前,promise得到解决,因此这引入了竞争条件. (2认同)

Cha*_*ani 1

由于您使用的是 Promise,因此要访问 Promise 返回的数据,请使用回调语法

function appInstallerDetailCtrl($scope, $routeParams, Data) {
  $scope.appId = $routeParams.appId;
   Data.then(function(returnedData) {
        $scope.apps=returnedData;
        console.log($scope.apps);
        for (var i in $scope.apps)
           console.log(i)   
   });   
}
Run Code Online (Sandbox Code Playgroud)

确保这一点

defer.resolve(result.data.versions.version);
Run Code Online (Sandbox Code Playgroud)

解析返回数组,以便上述代码正常工作。或者查看数据中的内容并调整控制器代码。