2

In my application I've tabs which loads form in each tab with certain amount of controls in it! Some tab will have huge number of controls and some might have less. So what I was trying to achieve is to show a loader when contents are being rendered into html. These contents will always be loaded at page load but rendering happens only when the tab is clicked. See the below code!

$scope.activetab = function (tabname) {
   showLoader();
   if (tabname == 'General')
      $scope.GeneralAct = true;
   else if (tabname == 'Contact Information')
      $scope.ContactAct = true;
   else if (tabname == 'Position/Hierarchy')
      $scope.PositionAct = true;
   else if (tabname == 'Ids and Program Access')
      $scope.IdsAct = true;
   else if (tabname == 'Equipment')
      $scope.EquipmentAct = true;
   else if (tabname == 'Licensing')
      $scope.LicensingAct = true;
};

function showLoader()
{
     $('body').append('<div class="loader"></div>');
}

What is happening is showLoader does not work until the render gets completed.

Now when I put alert after showLoader it will fire and loader will be shown. What I feel is the alert stops current execution of code and waits for user response for the alert and it'll render whatever has been executed until then! So is there any work around so that I can show the loader which does not wait for other html contents to render?

What I have tried so far?

  • Tried returning a promise from showLoader function and tried to wrap the if else code in $.when and .then functionality.
  • Tried keeping <div class='loader'></div> in body instead of appending it everytime and tried to toggle or fadeIn/fadeOut it in showLoader.
  • Tried keeping if else in setTimeOut to execute it after some fraction of seconds.

But unfortunately all the results returned with no success! If any workaround to show the loader is there, then I will be gladful to implement it!

Guruprasad J Rao
  • 29,410
  • 14
  • 101
  • 200
  • 2
    Don't append stuff with jQuery when working with Angular. Try to stop thinking in jQuery and adopt Angular's methods. Write your div in the DOM and hide/show it on condition. `
    `
    – Jeremy Thille May 26 '15 at 11:30
  • @JeremyThille.. Can you please give me a bit elaborated example! – Guruprasad J Rao May 26 '15 at 11:31
  • Well it's basic Angular stuff. It's actually the second thing you tried : Tried keeping
    in body instead of appending it everytime and tried to toggle or fadeIn/fadeOut it in showLoader. That's the way to go. Your showLoader() function is out of Angular's world. It's not in the scope. That's why its execution is delayed. Try $timeout( function(){ showLoader=true; })
    – Jeremy Thille May 26 '15 at 11:36

1 Answers1

4

First of all read the answer to this question, concentrating on part 2 (edit: and part 1 is pretty relevant... and everything else). The specific issue in your case, never use jQuery to maniuplate the DOM in Angular. That is the job of the model.

While specific cases might seem benign you are going against the intended flow of data in your app. Angular wants strict MVC, controller and view should ONLY communicate via the model because otherwise you can end up with messy and conflicting code. For example you might get cases where changes from Angular overwrite things jQuery has done, or vice versa, and it gets hard to control when and if this happens.


On to the actual question. A browser does not re-render constantly and javascript blocks rendering. What this means is that it won't actually render anything until the current stack of javascript code has finished running. So even though you insert the loader first it the browser will not show it until the rest of the code is finished, and since the rest of the code inserts your Angular content both will render at the same time.

You can't really get around this issue, but usually you don't need to. If you need to use a loader it is probably not because of the rendering time itself, it's because you have a http request or something else that is fired when the tab changes, and you are looking to show the loaded until that is complete. Is that what is happening here?

In that case I would suggest putting the loader inside the html of the tab and then have Angular remove it once the tabs content is loaded. So something like this inside the tab html, with css to make sure the loader is only shown when the tab is open:

<div class="loader" ng-hide="dataIsLoaded"></div>

It is however a bit hard to know what you problem is. If the data is only loaded and it's just the renderingtime that this won't help for the reason mentioned above, it won't render until the js is done running. But that is also a strange use case. Very seldom is the rendering itself so slow that you need a loader just for it.

Edit: If your issue actually is that your controller is so large and complex (or you have so many simultaneous controllers) that rendering becomes slow, then you should really be trying to fix that first. Most likely your controller could be significantly simplified so that you don't get these issues.

Community
  • 1
  • 1
Erik Honn
  • 7,576
  • 5
  • 33
  • 42
  • The data will always be loaded at the page load but only the rendering happens on `tab click` and because of huge amount controls to render the application becomes irresponsive which is why I want to show `loader` here! Will definitely try to implement your suggestion! +1 for the effort.. – Guruprasad J Rao May 26 '15 at 11:45
  • 2
    In that case I am afraid it probably won't work, the same issue with the stack needing to finish will make it not render. You have probably hit a limitation in Angular where having too many scopes will cause slowdowns. This is usually caused by havintg too much stuff active at once. Post more of your controller in the question and I might be able to help more. – Erik Honn May 26 '15 at 11:49
  • I think too that you may hit the angular's threshold, so it'll behave slower. A plunker of your controller will be helpful. – Vignesh May 26 '15 at 11:51
  • @Erik. My controller is a huge one where one method is connected to another so If I try to post it I cannot leave any part of it which will make question messier. So I just want to clear one more doubt here! Is it possible to do something like stopping process which alert does. Basically alert stops process until user provides some response, but I just want to stop process to some fraction of seconds!! Is it possible? – Guruprasad J Rao May 26 '15 at 11:56
  • 1
    Try a plunker if your controller is too big for the question. But yes, there are a few ways. You can wrap everything after showLoader in a $timeout to make sure it halts and lets a rendering step in between. However this may not be reliable since you can't fully control when a render step will happen. There are probably more ways, like trying an ng-init on the loader to trigger the rest of the code only after it has been inserted, but I would advice against any such method. You are slowing rendering down further. Better to clean up your controller so you don't have the issue in the first place – Erik Honn May 26 '15 at 12:26
  • 1
    "My controller is a huge one where one method is connected to another" <-- This is most likely the issue you should be trying to solve, not your rendering issue. – Erik Honn May 26 '15 at 12:26
  • @ErikHonn.. I agree with you buddy!! Will try cleaning up controller once.. Thanks for the suggestion and your time.. much appreciated! – Guruprasad J Rao May 26 '15 at 12:53