强制AngularJS服务在加载控制器之前返回数据

34

我在 Angular 中有一个服务,它使用我的 API 获取用户信息并提供给我的控制器。它是这样设置的:

angular.module('myApp', ['myApp.filters', 'myApp.services', 'myApp.directives', 'ngResource', 'infinite-scroll', 'ui.bootstrap', 'ngCookies', 'seo'])
  .service('userInfo', function($http, $cookies){
    $http.get('/api/users/' + $cookies.id).
    success(function(data) {
      var userInfo = data.user[0];

      return userInfo;
    });
  }). // other stuff comes after this

在我的控制器中,我这样包含它:

function userProfile($scope, $cookies, userInfo, $http, $resource, $routeParams, $rootScope){
    $scope.user = userInfo;
    console.log('user info is')
    console.log(userInfo);

如果我将相同的服务函数放在控制器本身中,则会正常返回数据,但是现在没有返回任何数据。我错过了什么?以前从未在Angular中使用DI/服务,可能有一个简单的错误。

我需要确保服务在控制器加载之前返回数据。如何实现这一点?


6
你可以这样简化你的console.logs: console.log('用户信息是', userInfo); - Eddie Monge Jr
@EddieMongeJr 真的吗?我不知道,谢谢。 - JVG
4个回答

42

你可以像这样使工厂返回一个Promise:

angular.module('myApp', ['myApp.filters', 'myApp.services', 'myApp.directives', 'ngResource', 'infinite-scroll', 'ui.bootstrap', 'ngCookies', 'seo'])
    .service('userInfo', function ($http, $cookies) {
    var promise = $http.get('/api/users/' + $cookies.id).
    success(function (data) {
        var userInfo = data.user[0];
        return userInfo;
    });
    return promise;
}) // other stuff comes after this

并且在你的控制器中,执行以下操作

function userProfile($scope, $cookies, userInfo, $http, $resource, $routeParams, $rootScope){
    userInfo.then(function(data){
        $scope.user = data;
    });
}

这可以保证无论何时使用服务,它都会以 同步 的方式提供数据,您不必在加载控制器之前加载任何数据。


6
承诺可以直接分配给$scope。当承诺被解决时,Angular会自动更新视图。 - Joseph Silber
6
请注意,在 Angular 1.2+ 版本中,承诺(promises)不会自动展开。 - Preston Marshall
这真的有效吗?我看到注入的是承诺,而不是来自响应的实际数据。 - Jakub Kuchar

17

您的userInfo服务必须返回$http返回的Promise。然后该Promise将被注入到您的控制器中,视图将在Promise成功解析后立即更新。


如果您不希望在userInfo解析之前渲染视图,则应在路由上设置一个resolve属性,并在那里注入您的服务:

$routeProvider.when('/profile', {
    templateUrl: 'profile',
    controller: userProfile,
    resolve: {
         userInfoData: function ($q, userInfo) {
             return userInfo;
         }
    }
});

接着,在你的控制器中将userInfoData注入代替userInfo服务。


1
我需要让所有的控制器/路由都能够使用这些数据,有没有一种特定的方式可以注入它,以便我不必为每个路由重复20多次? - JVG
@Jascination - 据我所知没有这样的方法。我认为你只需要在所有需要它的路由中设置 resolve: resolveUserInfo 即可。 - Joseph Silber
有趣。我尝试了您的解决方案,但仍然出现相同的问题-在数据解析之前加载控制器。 我知道这可能不应该是这种情况,因为存在“resolve”,我认为这是一个异步/回调问题,因为“resolveUserInfo”函数在返回之前没有完成加载数据。 - JVG
@Jascination - 你是否已经从服务中返回了 $http 承诺(promise)? - Joseph Silber
有没有不使用路由的方法来完成这个任务?http://stackoverflow.com/q/29889040/4341650 - downhand
这是一个不错的技巧。帮助我在页面之间实现平滑过渡。 - Meliodas

0

我曾经面临过同样的问题,需要使用$resource将数据加载到服务中,并将该数据传递给控制器 $scope。 我不想直接返回 promise 然后在控制器中使用解析器(如 @zsong 所写的),而是要在服务中完成所有操作,并使用@Joseph Silber 指出的直接将 promise 分配给 $scope 的能力来完成所有操作,所以这就是我所做的:

return function($scope){promise.then(function(data){$scope.user = data})};

并在控制器中像这样使用:

userInfo($scope);

虽然并没有太大的改进,但在我的控制器中它允许我使用更清晰的语法,希望即使三年后也能对您有所帮助!


0

你可以将资源对象分配给变量,就像这样 return $http.get(url)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接