Способы загрузки данных в контроллер через сервис в AngularJS

у меня есть сервис, который загружает данные с помощью $ http и возвращает обещание (упрощенное для краткости):

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

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

вот представление, которое использует эти данные, после того, как обещание разрешено и результат хранится в $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;
  });
}]);

вопросы

  • существуют ли другие широко используемые способы сделать это? Если да, проиллюстрируйте это примером кода.
  • каковы ограничения каждого подхода?
  • каковы преимущества каждого подхода?
  • при каких обстоятельствах я должен использовать каждый подход?

2 ответов


тестирование

Вариант 1: Использование разрешений делает насмешливые зависимости в модульных тестах контроллера очень простыми. В вашем первом варианте:

$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 некоторая произвольная переменная перед инициализацией. Очень грязно.

предпочитаю решает

хотя вы можете выполнять начальную загрузку данных в контроллерах, решения уже делают это намного чище и декларативнее.

однако у ngroute resolver по умолчанию отсутствует несколько ключевых функций, наиболее заметным из которых является зависимое разрешение. Что делать, если вы хотите предоставить 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);
  }
}

вы можете взломать это, загрузив usualStore от контроллера после customer вводят, но зачем, когда это можно сделать чисто в 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);
  }
}

есть ли другие часто используемые способы сделать это?

зависит, если у вас есть данные, которые находятся в другом домене, и это может занять время загрузки, поэтому вы не можете показать представление, пока оно не будет получено, поэтому вы пойдете на решение одного i.e первый.

каковы ограничения каждого подхода?

ограничение использования первого шаблона решением может быть то, что страница ничего не будет отображать, пока все данные не будут загружен

ограничение второго заключается в том, что получение данных может занять больше времени, и ваше представление будет похоже на " {{}}", Если вы не справились с ним с помощью css

каковы преимущества каждого подхода?

преимущество первого - это то, что я сказал ранее, что вы разрешите данные и убедитесь, что они присутствуют перед представлением

при каких обстоятельствах я должен использовать каждый подход?

решение очень полезно, если нам нужно загрузить некоторые данные, загруженные до инициализации контроллера и визуализации представления

а второй-когда у вас нет чеков, и эти проблемы с загрузкой ожидаются, и данные находятся в ваших руках !