【问题标题】:Ways of loading data into controller via service in AngularJS通过 AngularJS 中的服务将数据加载到控制器中的方法
【发布时间】:2015-02-17 18:52:41
【问题描述】:

我有一个服务,它使用 $http 加载数据并返回一个承诺(为简洁起见):

angular.module('myApp').factory('DataService', ['$http', function($http) {
  function unwrapFriendList(data) {
    ...
    return unwrappedFriendList;
  }

  return {
    getFriendList: function() {
      return $http.get('/api/friends').then(unwrapFriendList);
    }
  }
}]);

这是一个使用该数据的视图,在 promise 被解析并将结果存储在 $scope.friends 之后:

<div ng-repeat='friend in friends'>
  {{friend.firstName}} {{friend.lastName}}
</div>

在将数据加载到控制器时,我遇到了几种方法。

选项 1:使用通过 ng-route resolve 加载的数据的控制器

angular.module('myApp').controller('FriendListCtrl', ['$scope', 'friendList', function($scope, friendList) {
  $scope.friends = friendList;
}]);

路线部分:

angular.module('myApp', ...).config(function($routeProvider) {
  $routeProvider
    .when('/friends', {
      templateUrl: 'views/friends.html',
      controller: 'FriendListCtrl',
      resolve: {
        friendList: ['DataService', function(DataService) {
          return DataService.getFriendList();
        }]
      }
    })
    ...
});

选项2:自行触发数据加载的控制器

angular.module('myApp').controller('FriendListCtrl', ['$scope', 'DataService', function($scope, DataService) {
  DataService.getFriendList().then(function(friendList) {
    $scope.friends = friendList;
  });
}]);

问题

  • 还有其他常用的方法吗?如果有,请用代码示例说明。
  • 每种方法的局限性是什么?
  • 每种方法的优势是什么?
  • 在什么情况下应该使用每种方法?

【问题讨论】:

    标签: angularjs angularjs-service angularjs-routing


    【解决方案1】:

    单元测试

    选项 1: 使用 resolves 使得在控制器单元测试中模拟依赖变得非常简单。在您的第一个选项中:

    $routeProvider
      .when('/friends', {
        templateUrl: 'views/friends.html',
        controller: 'FriendListCtrl',
        resolve: {
          friendList: ['DataService', function(DataService) {
            return DataService.getFriendList();
          }]
        }
      })
    
    angular.module('myApp')
      .controller('FriendListCtrl', ['$scope', 'friendList',
        function($scope, friendList) {
          $scope.friends = friendList;
        }]);
    

    由于friendList 被注入到控制器中,因此在测试中模拟它就像将一个普通对象传递给$controller 服务一样简单:

    var friendListMock = [
      // ...
    ];
    
    $controller('FriendListCtrl', {
      $scope: scope,
      friendList: friendListMock
    })
    

    选项 2: 您不能使用第二个选项来执行此操作,并且必须监视/存根 DataService。由于第二个选项中的数据数据请求会在创建控制器时立即调用,因此一旦您开始执行多个、有条件或依赖(稍后会详细介绍)数据请求,测试就会变得非常复杂。

    视图初始化

    选项 1: 在完成所有解析之前,解析会阻止视图初始化。这意味着视图中任何需要数据(包括指令)的东西都会立即拥有它。

    选项 2: 如果控制器中发生数据请求,视图将显示,但在请求完成之前不会有任何数据(这将在未来的某个未知时间点)。这类似于一闪而过的无样式内容,可能会很刺耳,但可以解决。

    当您的视图中有组件需要数据但没有提供数据时,真正的复杂情况出现了,因为它们仍在被检索。然后,您必须通过强制每个组件等待或延迟初始化一段未知的时间来解决这个问题,或者在初始化之前让它们$watch 一些任意变量。很乱。

    首选解决

    虽然您可以在控制器中进行初始数据加载,但 resolves 已经以更简洁、更具声明性的方式进行。

    然而,默认的 ngRoute 解析器缺少一些关键特性,最值得注意的是依赖解析。如果您想向控制器提供 2 条数据:一个客户,以及他们通常商店的详细信息,该怎么办?这对 ngRoute 来说并不容易:

    resolve: {
      customer: function($routeParams, CustomerService) {
        return CustomerService.get($routeParams.customerId);
      },
      usualStore: function(StoreService) {
        // can't access 'customer' object here, so can't get their usual store
        var storeId = ...;
        return StoreService.get(storeId);
      }
    }
    

    您可以通过在注入customer 后从控制器加载usualStore 来解决这个问题,但是当它可以在ui-router 中通过依赖解析干净地完成时,为什么还要麻烦:

    resolve: {
      customer: function($stateParams, CustomerService) {
        return CustomerService.get($stateParams.customerId);
      },
      usualStore: function(StoreService, customer) {
        // this depends on the 'customer' resolve above
        return StoreService.get(customer.usualStoreId);
      }
    }
    

    【讨论】:

    • 感谢您的详细解答。 1)。我不能注入一个模拟版本的 DataService 吗?为什么这比注入一个模拟的朋友列表更糟糕? 2)。我可以通过将这些调用封装在服务逻辑中并将结果组合到一个对象中来部分缓解依赖解析问题吗?你能看出这种方法有什么问题吗?
    • 1.你可以,但也有人认为提供模拟数据比模拟服务更容易、更直接。 2. 是的,您可以这样做,但最终您创建的函数的唯一目的是将一堆解析绑定在一起。如果您将在应用程序的其他地方使用它们,那很好,但随着应用程序中服务数量的增长,否则会变得过多。
    【解决方案2】:

    还有其他常用的方法吗?

    视情况而定,如果您有其他域上的数据并且加载可能需要一些时间,因此在收到之前您无法显示视图,因此您将寻求解决一个,即第一个。

    每种方法的局限性是什么?

    使用第一种模式的限制可能是在加载所有数据之前页面不会显示任何内容

    第二个的限制是数据可能需要更长的时间才能收到,如果你没有用 css 处理它,你的视图会像“{{}}”

    每种方法的优点是什么?

    第一个的优点是我之前说过,您将解析数据并确保它在视图呈现之前存在

    我应该在什么情况下使用每种方法?

    如果我们需要在控制器初始化和渲染视图之前加载一些加载的数据,则解析非常有用

    第二个是当您没有签到并且预期这些加载问题并且数据掌握在您自己手中时!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-08-29
      • 2016-04-29
      • 1970-01-01
      • 1970-01-01
      • 2014-11-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多