Monday, July 18, 2016

Data for listing page and detail page without 2 API calls

Leave a Comment

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> 

2 Answers

Answers 1

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

Answers 2

I would change the service to cache the data. With $q.when() you can return a promise from a variable. So you save your response in a variable, and before doing the API call you check if the cache has been set. If there is any cache, you return the data itself. Otherwise, you do the usual promise call.

.service('ClientsService', function($http, $q) {     var clients = null;      this.getClient = function(id) {         if (clients !== null) {             return $q.when(id ? clients[id] : clients);         }          var deferred = $q.defer();         $http.get('http://api.icp.sic.com/clients').then(function(response) {             clients = response.data;             deferred.resolve(id ? clients[id] : clients);         }, function (response) {             // will handle error here         });          return deferred.promise;     } }) 
If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment