9

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

我在 egghead.io 上看到了如何在多个控制器之间共享数据,并且我还阅读了这个 StackOverflow 问题:“ Sharing a variable between controllers in angular.js ”。

但是,那里的答案不使用该$http模块。使用$http时,控制器没有数据可以处理,当收到响应时已经太晚了。

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

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

  1. 每个控制器都会触发$http请求,获取已经在另一个控制器中使用过的相同数据;和,
  2. 如果我尝试操纵收到的数据,我就有了一个then功能。

下面你可以看到我的代码:

控制器.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);
}

应用程序.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'});
}]);

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

谢谢

4

4 回答 4

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

在 promise 被解析/拒绝后注册的任何回调都将立即以相同的 result/failure_reason 被解析/拒绝。一旦解决/拒绝,promise 就不能改变(它的状态)。因此,第一个调用的控制器getApps()将创建 Promise。任何其他调用的控制器getApps()将立即获得返回的承诺。

于 2013-08-22T14:50:50.030 回答
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)   
   });   
}

确保这

defer.resolve(result.data.versions.version);

解析返回数组,以使上述代码正常工作。或者查看数据中有什么并调整控制器代码。

于 2013-08-22T10:12:39.360 回答
1

我发现不确定天气的方式是否是最好的方法。

在 HTML 中

<body ng-app="myApp">
  <div ng-controller="ctrl">{{user.title}}</div>
  <hr>
  <div ng-controller="ctrl2">{{user.title}}</div>
</body>

在 Javascript 中

 var app = angular.module('myApp', []);
   app.controller('ctrl', function($scope, $http, userService) {
      userService.getUser().then(function(user) {
        $scope.user = user;
      });
    });

   app.controller('ctrl2', function($scope, $http, userService) {
      userService.getUser().then(function(user) {
        $scope.user = user;
      });
    });

   app.factory('userService', function($http, $q) {
    var promise;
    var deferred = $q.defer();
      return {
        getUser: function() {
          if(!promise){     
          promise = $http({
              method: "GET",
              url: "https://jsonplaceholder.typicode.com/posts/1"
            }).success(function(res) {
                data = res.data;
              deferred.resolve(res);
            })
            .error(function(err, status) {
              deferred.reject(err)
            });
          return deferred.promise;
          }
          return deferred.promise;
        }
      }
    });

这只会发出 1 个 HTTP 请求。

于 2016-09-21T13:38:04.873 回答
0

我的问题是我不想resolve在加载另一个控制器之前等待,因为如果网络速度很慢,它会显示控制器之间的“滞后”。我的工作解决方案是通过ui-router's在控制器之间传递一个承诺,params并且来自承诺的数据可以异步加载到第二个控制器中,如下所示:

app.route.js - 设置要传递给 SearchController 的可用参数,显示搜索结果

        .state('search', {
            url: '/search',
            templateUrl: baseDir + 'search/templates/index.html',
            controller: 'SearchController',
            params: {
                searchPromise: null
            }
        })

Landing.controller.js - 用户添加搜索输入并提交的控制器

    let promise = SearchService.search(form);
    $state.go('search', {
        searchPromise: promise
    });

search.service.js - 从用户输入返回承诺的服务

    function search(params) {
        return new Promise(function (resolve, reject) {
            $timeout(function() {
                resolve([]) // mimic a slow query but illustrates a point
            }, 3000)
        })
    }

search.controller.js - 在哪里搜索控制器

    let promise = $state.params.searchPromise;

    promise.then(r => {
        console.log('search result',r);
    })
于 2017-01-17T10:01:01.627 回答