12

Does anyone know the good solution for implementing Angular 2+ pre loader with percentage indicator ( Similar to Gmail's loading screen )?

I know that usual way is to add <div> inside our <app-root> and style it, maybe even add CSS animations and it will be replaced by app content once app is loaded.

But... What I am actually looking is to display animated splash screen ( SVG or whatever else ) where after animation completes loading bar should appear and show progress status.

enter image description here

At first point I was thinking about separate splash component that will be only component eagerly loaded and from there load all other modules but if I map that component to '/' how to display it on any other route as a first ( starting point ). Also, this means that Angular main bundle must be already loaded so this is not a good option.

Most likely this question is too broad and not suitable for Stack Overflow but I can't find anywhere a good solution for this. :(

Is there a way to load plain JavaScript without Angular that will load Angular and display progress? Or any other ( better ) idea?

This must be achievable since whole Gmail is Angular app and they have it :D

Shubham Verma
  • 8,783
  • 6
  • 58
  • 79
Milos Miskone Sretin
  • 1,748
  • 2
  • 25
  • 46

6 Answers6

8

You may try ngx-progressbar, it is really cool. The API is not trivial but well documented, so you may build the progress bar of any complexity.


UPD After discussion I would suggest following approach (index.html)

1) Provide progress bar on the html level:

<my-app>
  <div style="width: 100%; background-color: grey;">
    <div id="myProgressBar" style="width: 1%; height: 30px; background-color: green;">
    </div>
  </div>
</my-app>

2) Load your app bundle and inject it into DOM manually via XMLHttpRequest

const tag = document.createElement('script');
const xhr = new XMLHttpRequest();
xhr.open('GET', 'my-angular-app-bundle.js?' + new Date().getTime());
xhr.onloadend = (e) => document.head.appendChild(tag);
xhr.send(); 

3) Use XMLHttpRequest.onprogress to watch the progress and to update progress bar params

const barElement = document.getElementById('myProgressBar');
xhr.onprogress = (e) => {
  if (e.lengthComputable) {
    const width = 100 * e.loaded / + e.total;
    barElement.style.width = width + '%';
  }
}

To make onprogress updates smoother you may increment progress bar width in a setInterval loop:

if (e.lengthComputable) {
  const endWidth = 100 * e.loaded / + e.total;
  const intervalId = setInterval(() => {
    const width = parseInt(barElement.style.width, 10);
    if (width >= endWidth) {
      clearInterval(intervalId);
    } else {
      width += 2; 
      barElement.style.width = width + '%'; 
    }
  }, 40);
}
dhilt
  • 18,707
  • 8
  • 70
  • 85
  • It's really cool but I still need Angular to be loaded in order to use it. It's nice if I am displaying that for loading lazy loaded modules in app. – Milos Miskone Sretin Jun 19 '18 at 02:53
  • What I am thinking is to load whole app ( AOT compiled ) without lazy loading but to display progress before Angular is loaded. After Angular is loaded all modules will be there. Check Gmail to see how it should be. – Milos Miskone Sretin Jun 19 '18 at 02:55
  • 1
    @MilosMiskoneSretin I drafted a little demo, could you try it? https://stackblitz.com/edit/angular-ygfgju Does it look like something you need? All magic is in index.html. – dhilt Jun 19 '18 at 03:08
  • It's more like a workaround :( This does not display real loading progress. I need to calculate Angular bundle size and display real percentage :( – Milos Miskone Sretin Jun 19 '18 at 03:25
  • I am now thinking to bootstrap only SplashScreen component and use it as entry component which will trigger loading of other modules and display loading progress but still don't know how to do it right :( – Milos Miskone Sretin Jun 19 '18 at 03:51
  • @MilosMiskoneSretin Hey! I updated the answer with the approach that, I think, is pretty close to what you need – dhilt Jun 19 '18 at 04:09
  • Nice approach. Thanks, let me check if this can fit in totally before approving the answer. – Milos Miskone Sretin Jun 19 '18 at 18:30
  • It does some pretty good aproach, but still, with slow connections the bar will get 100% before actually loading all the app – Nery Ortez Jul 09 '18 at 21:42
  • @NeryOrtez You are absolutely right, I updated the answer, now `setInterval` is being used only for regular width changing each time the `onprogress` handler is being called. – dhilt Jul 10 '18 at 16:01
  • in step 2), how does whatever the request returns land inside the created script tag ? – Nikita Fuchs Jun 03 '20 at 12:34
3

I have used PACE in my angular app to show the progress bar for the splash screen. You can also show as you want. You need to go through with below link:

Automatic page load progress bar

Hope it will help you.

Shubham Verma
  • 8,783
  • 6
  • 58
  • 79
2

Sometimes tag-based loading is more efficient, you could use PreloadJS library. You could also manually track end of script loading using onload event and artificially interpolate progress to make impression that app is loading at constant speed. Gmail probably does not show actual progress at all, it just waits end of loading displaying fake steady progress using timers.

kemsky
  • 14,727
  • 3
  • 32
  • 51
  • I tested with different connection speed and Gmail seems to use real percentage. Also, if I use PreloadJS, do you have suggestion to implement it with AngularCLI since CLI on compile time adds JS bundle links automatically and here I'll need to load them manually? – Milos Miskone Sretin Jul 05 '18 at 15:45
  • 2
    cli allows embedding global scripts (i.e. PreloadJS), but i could not find any way to override app script tags generation, probably you have to write custom script and run it after `ng build` i.e. in `scripts` section of `package.json`. – kemsky Jul 05 '18 at 16:03
2

I don't think that loading indicator shows the loading of the app. I think it shows the loading of the data after the app has loaded. Think of data like mails, contacts, etc... I think that gmail loading is split in two parts.

Part 1: Show a simple animation (without loading indicator) while that app itself starts.

Part 2: Now the app has started keep displaying that loading annimation. Next, inventorise how many data requests you need to make and add a loading bar for this.

You can use this for the startup annimation.

Robin Dijkhof
  • 18,665
  • 11
  • 65
  • 116
1


maybe my answer is a little late, but you can check this tutorial, i think it's very realistic and useful implementation:

Implementing Simble PRogress Bar Angular/Server side

sohaieb azaiez
  • 768
  • 9
  • 20
0

The only way I think of is connecting your app to the CLI and display the progress it shows from the command line itself.

KLTR
  • 1,263
  • 2
  • 14
  • 37