Link to home
Start Free TrialLog in
Avatar of Camillia
CamilliaFlag for United States of America

asked on

Angular - "nest" service calls?

I have a page with a button on it. User clicks on it and a modal opens. In the modal, I have some text fields and some dropdowns. I set the dropdown values to the selected value that I read from the database.

This is the issue: sometimes when I click on the button, the dropdowns aren't populated. I close the modal, open it and I see them populated but again not always.

Someone at work said I need to "nest" the calls that populates the dropdown values and sets their selected value but I don't know what that means. Someone else said I need to change the order of the service calls that populates the dropdown values and then the selected value. Another one said I need to put a timeout on  
contactService.getContactDetails(contactId)

Open in new window

to make sure first the dropdowns are populated.

This is what I have. How can I change it? This is ContactController.js. I have other code in there like save and delete but I will leave those out. I'm typing by hand so I might have some typos.

(function () {
  'use strict';

  app.controller('ContactController,ContactController);
  ContactController.$inject = [...]

  function ContactController(...)
 {
     $scope.saveChanges = function ()
   { ...}

   activate();
   function activate()
    {
       contactService.getContactDetails(contactId)
        .then(function (ret) {

             $scope.contactDetailInformation = ret; //this gets the contact detail for Id xyz
          
             $scope.selectedDist = _find($scope.dists, {name: ret.DistName}); //I set the selected value that this user has
            $scope.selectedstate = _find($scope.allStates, {name: ret.StateId});

           $scope.selectedCountry = _find($scope.allCountries, {name: ret.countryId});

          $scope.selectedStatus = _find($scope.allStatuses, {name: ret.statusId});

         
       });

      //then here I get the dropdown values
          contactService.getStates
           .then(function (ret) {
              $scope.allStates = ret;
           });

         contactService.getCountries
           .then(function (ret) {
              $scope.allCountries = ret;
           });
             

            contactService.getDists
           .then(function (ret) {
              $scope.dists = ret;
           });

          contactService.getStates
           .then(function (ret) {
              $scope.allStates = ret;
           });

              contactService.getStatsues
           .then(function (ret) {
              $scope.allStatuses = ret;
           }); 
 
   }    
   

})();

Open in new window

Avatar of Julian Hansen
Julian Hansen
Flag of South Africa image

Firstly you don't want to nest anything - that is just wrong. You are making ASYNCH calls to the server - the key word here is Asynch.

Angular handles the two way binding so all you need to do is kick off the service requests one after the other and in the then() functions set the state of the model.

If the modal is not populating it is possible that the data has not arrived yet. Have you checked the console to see if the data is still being downloaded.

Do you want the modal to only show once the the data has downloaded - or can it show before hand.

Have you considered generating a single service request that packages all the data you need and sends it back in one go so you don't have to have multiple round trips to the server?
Avatar of Camillia

ASKER

If the modal is not populating it is possible that the data has not arrived yet. Have you checked the console to see if the data is still being downloaded.

Yes, that's the problem. I'll debug it again. Only the dropdowns act that way. The textboxes are ok and it happens on and off. My coworker debugged it and said the order of the calls need to change because dropdown values are not populated yet.

Do you want the modal to only show once the the data has downloaded - or can it show before hand.
I click on the modal and data needs to be there. If data is loaded before modal is displayed, that's fine too. Does this mean I need to take it out of "activate"? I read "activate" is used for initialization.

Have you considered generating a single service request that packages all the data you need and sends it back in one go so you don't have to have multiple round trips to the server?
No. This is my first angular and I'm looking at other sections of the code and looking at tutorials... but the other developer...this is his second angular project and I'm sure he's not doing it totally correctly.

I'll debug it again but now, I still don't know how to fix it. I don't want to nest it even tho the other developer said so. Loading the data before modal is displayed is a good idea (take it out of activate? I'll debug it when I go to work soon)
Nesting does nothing but make the process take longer. Basically (if I understand correctly) the suggestion is to make first call - wait for it to finish then make second, wait for it to finish etc - totally goes against ASYNCH programming.

You should read up on promises (you can read more here). What you can do is kick off all the service requests - save the returned promises and then do a
Promise.all(function() {
    // activate modal here
})

Open in new window


I would seriously look at reducing the number of service requests being made - it might not make sense within the context of your app to do this but making multiple calls to the server for data that is all going into the modal might not be optimal.
Ok. Let me see. I was hoping for a quick solution since this needs to go to testing today but I'll look at promise.

I think you're right that number of calls needs to be reduced. I find these pages to be slow.

I'll see how I can fix this one first.
Just explain this bit to me

contactService.getStates
  .then(function (ret) {
  $scope.allStates = ret;
});

Open in new window


Where is getStates defined - can you show me that. You are using it like a property rather than a function - so it appears to be a promise.

Is the result for getStates the same for every user or does it change depending on the state of the model?
I have a service called ContactService.js.
In it, I have the database calls.

In ContactService.js, I have this

(function () {

angular.module ('app').service('ContactService',ContactService);

ContactService. $inject = [...];

function ContactService(...)
  {
    var vm = this;

   vm.get Dists = function () {

   }

  vm.getStatuses = function () {

 }

vm.getStates = function () { // gets US states

return $http.get ('/call Mvc c# controller')
.then (function success (result) {
   return result.data;
}, function failure...
}

}
// and so on

})();

Open in new window


That code gets the United States and Canada  states for the dropdown in the modal. Always the same and then I set the selected value that user had already selected.

I'm reading about promise now and see what I can come up with. I really don't want to follow what the developer suggested...at least not yet.

Going thru this one too
http://andyshora.com/promises-angularjs-explained-as-cartoon.html
Also, if you look at my original post...first this is called getContactDetails

First the ddls need to be populated, then the getContactDetails called.

I have 4 ddls...I need a promise for each and then call that getContactDetails?

If so, how would I have 4 database calls in a promise and if success, then call getContactDetails?
ASKER CERTIFIED SOLUTION
Avatar of Julian Hansen
Julian Hansen
Flag of South Africa image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Thanks, let me look. I was just looking into $q.all and trying a sample code. I created a var promises = []; and used .push to put the database return result in it. Then got stuck on how to read them. I'll look at your example.

You're right about the list of states to be initialized. I think following what the other developer has done in angular is not a good idea since this is his second one and doesn't look like he's following best practices . But it is what it is.

I'll post back.
I understand your example, Julian. The promise section. I'll see how I can fit that into this code.

I'll leave this question open for now.

Thanks for the example.
I implemented it. I'll post here how I did it when I go home...it's hard to  post from my phone. I'd like for you to review it. I couldn't follow exactly what you have because of this code so it would be great if you review it.

I'll post back later tonight.
Julian,

1. What is the purpose of $scope.loading = true; and setting it to false?

2. What if the promise is not ready...the modal won't open? I think there's a way to check for failure, no? is there a way to put a timeout on it. For example, user clicks on the modal...wait 10 seconds to make sure the promise is populated.
1. That is just a flag to show and hide the loading screen

2. The modal opens only when all promises have been resolved. I did not do the failure side of it as this was just a demonstration of how to act on multiple promises - however checking for failure is important.

What action do you want to take in the event that one of the promises fails?

I will put up another demo that displays the failure option.
What should happen if all promises aren't resolved?I can ask at work but I know they don't know. Can there be a timeout...like wait 10 seconds...then open the modal? OR I don't think this is good and I'll be back to the first code I had...but if promise fails...still open the modal and call database again to populate (but  I don't think this is good)

The other developer nests the calls in another section where there's a modal with dropdowns but I didn't want to do that.

If I can see how a failure works...maybe I can think of what to do when promise failed.

The code looks much much cleaner than what I had now that I changed it to Promise. Thanks for that.
If one or more of the promises fail then the $q.all will fail and you can handle accordingly. Sample to follow shortly.
I'll find an example to do the failure part. Thanks for your help. I have another angular open Question,  if your have time.
You are welcome - will take a look tomorrow if it is still unsolved.
My other question is Unsolved. I finished the Internet Googling for an answer :)