C Ivemy C Ivemy - 4 months ago 31
AngularJS Question

Data for listing page and detail page without 2 API calls

I have set up a service to return a listing of clients from my API. Using UI-router, I can successfully pass a client's id to the details state - however, it seems unnecessary here to make another API call to retrieve a single client when I have all the necessary data in my controller.

What is the best way to use the ID in my detail state URL to show data for that client? Also - if a user browses directly to a client detail URL - I'll need to then make a call to the API to get just that client data - or is there a better way?

EDIT: I am not looking to load the two views on the same 'page', but completely switch views here, from a listing page to a detail page.

Routes in App.js

$stateProvider
.state('root', {
abstract: true,
url: '',
views: {
'@': {
templateUrl: '../partials/icp_index.html',
controller: 'AppController as AppCtrl'
},
'left-nav@root': {
templateUrl: '../partials/left-nav.html'
},
'right-nav@root': {
templateUrl: '../partials/right-nav.html'
},
'top-toolbar@root': {
templateUrl: '../partials/toolbar.html'
}
/*'footer': {
templateUrl: '../partials/agency-dashboard.html',
controller: 'AppController as AppCtrl'
}*/
}
})
.state('root.clients', {
url: '/clients',
views: {
'content@root': {
templateUrl: '../partials/clients-index.html',
controller: 'ClientsController as ClientsCtrl'
}
}
})
.state('root.clients.detail', {
url: '/:clientId',
views: {
'content@root': {
templateUrl: '../partials/client-dashboard.html',
//controller: 'ClientsController as ClientsCtrl'
}
}
})
// ...other routes


Service, also in app.js

.service('ClientsService', function($http, $q) {
this.index = function() {
var deferred = $q.defer();
$http.get('http://api.icp.sic.com/clients')
.then(function successCallback(response) {
console.log(response.data);
deferred.resolve(response.data);

},
function errorCallback(response) {
// will handle error here
});
return deferred.promise;
}
})


And my controller code in ClientsController.js

.controller('ClientsController', function(ClientsService) {
var vm = this;
ClientsService.index().then(function(clients) {
vm.clients = clients.data;
});
});


And finally, my listing page clients-index.html

<md-list-item ng-repeat="client in ClientsCtrl.clients" ui-sref="clients-detail({clientId : client.id })">
<div class="list-item-with-md-menu" layout-gt-xs="row">
<div flex="100" flex-gt-xs="66">
<p ng-bind="client.name"></p>
</div>
<div hide-xs flex="100" flex-gt-xs="33">
<p ng-bind="client.account_manager"></p>
</div>
</div>
</md-list-item>

Answer

You can use inherited states like suggested here.

$stateProvider
    // States
 .state("main", {
      controller:'mainController',
      url:"/main",
      templateUrl: "main_init.html"
  })  
  .state("main.details", {
      controller:'detailController',
      parent: 'main',
      url:"/:id",
      templateUrl: 'form_details.html'
  })  

Your service does not change. Your controllers check if the Model has been retrieved:

app.controller('mainController', function ($scope, ClientsService) {
  var promise = $scope.Model ? $q.when($scope.Model) : ClientsService.index();
  promise.then(function(data){
    $scope.Model = data;
  });
})
app.controller('detailController', function ($q, $scope, ClientsService, $stateParams) {
  var promise = $scope.Model ? $q.when($scope.Model) : ClientsService.index();
  promise.then(function(data){
    $scope.Model = data;
    $scope.Item = data[$stateParams.id];
  });
})

See http://plnkr.co/edit/I4YMopuTat3ggiqCoWbN?p=preview

[UPDATE] You can also, if you must, combine both controllers:

app.controller('mainController', function ($q, $scope, ClientsService, $stateParams) {
  var promise = $scope.Model ? $q.when($scope.Model) : ClientsService.index();
  promise.then(function(data){
    $scope.Model = data;
    $scope.Item = data[$stateParams.id];
  });
})
Comments