Preboot takes long time to transition from server to client

11 Feb 2018

Angular application can be pre-rendered on server side and served to the users, so that entire/most of the page sections appears instantly with data presenting a good user experience on initial loads (no more partial fetches that make your home page to dance as it loads laughing asynchronous sections). These kind of applications that has single source base compiled to a server compatible script ( like Node based Common JS modules ) which can generate HTML on server and then client browser compatible JavaScript version that can run them back on the client are termed as isomorphic JavaScript web applications i.e., single source multiple forms.

Note: If you already know about preboot and want to cut to the chase, just scroll down to the bottom of the article.

Well you may have a question, on how a pre-rendered HTML can spring back to life with all interactivity/event bindings. Well not exactly, the same Server HTML / View gets back to life on the browser. When you inspect the HTML elements, you will find that there will be a second hidden view created (app-root) on the browser, and all the events bindings & rendering happens within that hidden Element. When those initial app boot/bootstrap process is going on in the background, the user is shown with a server generated view.

Server generated view - is it just dummy ? Answer would be Yes, but no...

Yes, because it is not used on client as we expect it to be. No, because we have an Angular module (Angular Preboot) that makes it as useful as possible.

Preboot module project that was started as part on Angular Universal is now part of Core Angular module. That you can readily import and use... Preboot is not limited just to Angular, it can also be used for any isomorphic Javascript frameworks, that can create views on Server. Let us see how Preboot makes server views useful.

In normal case, when an HTML generated on the server side rendered on the browser and the actual application boot starts in parallel... the view or HTML within the app-root tag will be destroyed and the client view templates will be initialized and all process happens again. So this is How it appears to the end user  

  • application view appears complete for few seconds, giving user a false perspective that everything is loaded and ready to use... till the moment all dependent scripts gets downloaded on their browser.
  • users may try to interact with the page, clicking on the buttons or links so on., with no reaction or response from the application.
  • once scripts and render blocking resources are downloaded it immediately destroys the HTML within its app-root tag, causing the page to flicker as the view vanishes and the new view gets generated once again. This completely defeats the purpose of Server Side Rendering(SSR) to improve the perceived responsiveness. 

Case when Preboot module is ON,

  • application view looks complete, just like the previous one
  • users can interact and perform certain actions as they normally do. But more complex actions like form submissions will show a blocker screen making it evident that application is still loading. Not giving a false sense to the users.
  • when scripts are downloaded, it creates a duplicate app-root which is hidden from the user. All application bootstrapping happens within that in the background. If the required HTTP JSON response are already serialized and transferred from the server side. Then application can get back lively within a matter of milliseconds.
  • Once, the Preboot sees that the application bootstrap is done and application is in Stable state. It makes the switch... The client/browser generated view is displayed and the server generated view will be destroyed. So we will end up with a single app-root, the real applications.
  •  Preboot does offer more that this simple load and switch. It replays all the recorded user actions/interactions that took place on the server generated HTML. so user will mostly won't miss any interaction when the switch happens and it will be seamlessly done.

You can look in to more information on the module and its integration in below github project homepage
Angular Preboot

Slow transition of states from server to client issue :

Preboot normally makes the switch when the application completes the bootstrapping and the application is marked as Stable. This default behaviour works out for most of the applications as intended and cause issue with very few cases.

In one of my recent Angular 4+ project, it had tabbed layout with around 10 tabs in the page - all should be loaded based on the requirement. Each tab had components that depend upon AJAX request for data, which usually take upto 2 sec for each request to complete. Those data heavy components were not pre-rendered on server so the data for all 10 tabs should be fetched on the client.

We had few logics through which we fetched the data required to display for the active/visible tabs and delay the data fetch for the other tabs by 1000ms. The idea looked flawless, but after implementation it resulted in huge delay for application to get back to lively state...

Entire application just showed the server rendered HTML/view till it completes the data fetch for all tabs. Even though the first required data request completes within 2 secs, the preboot never switched to the real application root. So this is because preboot was waiting for the application to be stable, it considered all the AJAX request as the required or must have for stable system.

So to confirm the root cause, I commented out the preboot module and then the application started to load progressively and refresh & render the tabs as and when it receives corresponding data. But this fix resulted in the application flickering issue as we don't have preboot module enabled.

So browsing through simple preboot documentation, we found a way to manually influence the preboot to replay all recorded events and do the switch at the intended time. PrebootModule has exposed EventPlayer class that can be injected and used to invoke the replayAll() method that will do the switch immediately.

// In Angular 4
// app.module.browser.ts
import { BrowserPrebootModule } from 'preboot/browser';

imports: [...
  BrowserPrebootModule.replayEvents({ noReplay: true })
]

// In Angular 5+, In module registration
// app.module.ts
import { PrebootModule } from 'preboot';
imports: [ ...
  PrebootModule.withConfig({ appRoot: 'app-root', noReplay: true })
]
// Later injected and triggered from the service
// Angular 4
import { EventReplayer } from 'preboot/browser';

// Angular 5+
import { EventReplayer } from 'preboot';

@Injectable()
class Foo {
  constructor(private replayer: EventReplayer) {}

  // you decide when to call this based on what your app is doing
  manualReplay() {
    this.replayer.replayAll();
  }
}

So for my project, I triggered replayAll() method right after the active tabs have completed fetching the required data. This immediately switched/transitioned my application (app-root) to the real one(client rendered view), which allowed interactivity with the active tab as the background tabs were still loading further data. This improved the seamless loading in our application.

Even though Angular preboot module has done its best to automatically identify the moment to do the switch, sometimes it may requires us to manually identify and trigger as required.