Yesterday I described how to make use of the Ionic framework and IBM's MobileFirst platform. Today I'm going to build on that first post and talk about bootstrapping. Specifically - how can you coordinate the use of your application with the Cordova deviceReady and MobileFirst initialization routines.

To give some context, I first talked about this issue last August in a blog post, Ionic and Cordova’s DeviceReady – My Solution. In that post, I described how I wanted to coordinate the first view of my Ionic app with Cordova's deviceReady event. The solution I used there was to create a view that acted a bit like a splash screen. When the deviceReady event fired, I then pushed the user to the real initial page of the application and ensured they never went back to that initial temporary page.

At the time, that felt like a bit of a hack, but it also seemed sensible. However, other people suggested another approach. You can simply tell Angular to not bootstrap until deviceReady is fired. For whatever reason, that didn't seem as sensible as my approach back then, but now... yeah... now that makes sense. So how would we do this in a MobileFirst application?

The first thing to note is that within MobileFirst and hybrid applications, running WL.Client.init and using the success handler, wlCommonInit, gives you the same behavior as listening for deviceReady in a "regular" Cordova application. So basically - wlCommonInit is deviceReady. With that in mind, I began by removing the automatic bootstrap from my code. I changed:

<body ng-app="starter">

to

<body>

Then I modified my wlCommonInit to do a manual bootstrap:

var wlInitOptions = {
	
	// # To disable automatic hiding of the splash screen uncomment this property and use WL.App.hideSplashScreen() API
	//autoHideSplash: false
		 
};

if (window.addEventListener) {
	window.addEventListener('load', function() { WL.Client.init(wlInitOptions); }, false);
} else if (window.attachEvent) {
	window.attachEvent('onload',  function() { WL.Client.init(wlInitOptions); });
}

function wlCommonInit(){
	
	// Common initialization code goes here
	var env = WL.Client.getEnvironment();
    if(env === WL.Environment.IPHONE || env === WL.Environment.IPAD){
        document.body.classList.add('platform-ios'); 
    } else if(env === WL.Environment.ANDROID){
        document.body.classList.add('platform-android'); 
    }

    angular.element(document).ready(function() {
	    angular.bootstrap(document, ['starter']);
    });
	
}

This works perfectly. But there's a twist. By default, an application doesn't connect to your MobileFirst server until you tell it to. This lets you delay or even skip actually connecting unless you need too. I definitely needed too, so I added that code in and moved my bootstrap code to there:

var wlInitOptions = {
	
	// # To disable automatic hiding of the splash screen uncomment this property and use WL.App.hideSplashScreen() API
	//autoHideSplash: false
		 
};

if (window.addEventListener) {
	window.addEventListener('load', function() { WL.Client.init(wlInitOptions); }, false);
} else if (window.attachEvent) {
	window.attachEvent('onload',  function() { WL.Client.init(wlInitOptions); });
}

function wlCommonInit(){
	
	// Common initialization code goes here
	var env = WL.Client.getEnvironment();
    if(env === WL.Environment.IPHONE || env === WL.Environment.IPAD){
        document.body.classList.add('platform-ios'); 
    } else if(env === WL.Environment.ANDROID){
        document.body.classList.add('platform-android'); 
    }

    WL.Client.connect({
	onSuccess:function() {
		console.log("Connected to MFP");
		angular.element(document).ready(function() {
			angular.bootstrap(document, ['starter']);
		});			
	}, 
	onFailure:function(f) {
		console.log("Failed to connect to MFP, not sure what to do now.", f);	
	}
    });

}

The WL.Client.connect method does exactly what you expect - connect to the MobileFirst server. Seems like a small mod, right? But when I did this, I noticed something bad.

Untitled2

Can you see it? It is a FOUC (Flash of Unstyled Content). There's a simple fix for this though. We can simply use the splash screen to hide the site until things are ready. (And credit for suggesting this approach goes to Carlos again!) One of the initialization options you can provide is to not hide the splash screen. You can then use the JavaScript API to hide it. Here is that version.

var wlInitOptions = {
	
	// # To disable automatic hiding of the splash screen uncomment this property and use WL.App.hideSplashScreen() API
	autoHideSplash: false
		 
};

if (window.addEventListener) {
	window.addEventListener('load', function() { WL.Client.init(wlInitOptions); }, false);
} else if (window.attachEvent) {
	window.attachEvent('onload',  function() { WL.Client.init(wlInitOptions); });
}

function wlCommonInit(){
	
	// Common initialization code goes here
	var env = WL.Client.getEnvironment();
    if(env === WL.Environment.IPHONE || env === WL.Environment.IPAD){
        document.body.classList.add('platform-ios'); 
    } else if(env === WL.Environment.ANDROID){
        document.body.classList.add('platform-android'); 
    }

	WL.Client.connect({
		onSuccess:function() {
			console.log("Connected to MFP");
			angular.element(document).ready(function() {
				WL.App.hideSplashScreen();
				angular.bootstrap(document, ['starter']);
			});			
		}, 
		onFailure:function(f) {
			console.log("Failed to connect to MFP, not sure what to do now.", f);	
		}
	});

}

In the code snippet above, you can see the new option added to wlInitOptions and then the call to WL.App.hideSplashScreen to hide the screen. This worked too - but don't forget that the mobile browser simulator does not have a splash screen! At first I thought this workaround wasn't working, but then I tested in the iPhone Simulator and it worked perfectly.

So - thoughts on this approach? Does it make sense? I've recorded a video of this process that you can watch below. I'm also attaching a copy of my common folder here: Download