What’s in an SPA? Part II, the A in SPA

As you may remember from the first part of this thrilling SPA saga, the only object I’ve invoked right now is the Application. What is this Application that everyone in the ‘hood is talking about?

For starters it should be your main dispatcher for all the objects in your application. Remember, we’re not doing a web page or a bunch of html files with some javascript, but an application. Think of it as of your safehouse. Whenever you’re lost (you shouldn’t but you will be) in the maze of your code you can reach up to your fair objApp variable and crawl down from it to known territories. For example, to execute the orderMoreTunnel() method of the Tunnel module.

Your app should start by loading its own configuration. This config data can be stored into a .json file in your server or in a local directory of your device. Sure, you can store it in a database, but then you’ll need to have another configuration file with the details to access that database. So, let’s keep it simple and let’s create a simple maintainable configuration file. For advanced users, you’ll usually will create this file using grunt or another build tool, extracting the details from… well, maybe a database?

It doesn’t matter is you create the configuration by hand or using a digital minion: it’s useful to have this information isolated from your code. This way you can change the basic behaviour of your app class for each different project just by changing the configuration.

A sample configuration file can be organized like this:

{
    "appName": “myAwesomeApp”,
    "language": "en",
    "mustAuthorize": true,
    "encryptData": true,
    
    "services": [
        {
            "service": "auth",
            "url": "https://api.myapp.com/login"
        },

        {
            "service": "weather", 
            "url": "http://api.worldweatheronline.com",
            "key": "XXXXX"
        },

        {
            "service": "maps",
            "url": "https://maps.googleapis.com/maps/api/js"
        }
    ],

    "home": {
        "hasMenu": false,
        "//": "this measure is in seconds",    
        "timeToRefresh": 60
    },

    "modules": [
        {
            "first": true,
            “id”: "measures",
            "config”: {
            }
        },

        {
            “id": "events",
            "config": {
            }
        },
        
        {
            “id”: "prefs"
        }
    ]
}

In the first block of elements I defined some data that the app will import immediately into its own globals structure and will make available to the rest of the program thru an ad hoc method. After that I define the services my app will need to access. For example, a webpage to authenticate the user, a weather service (since this sample app will display the weather in my city), or the Google Maps.

In the home section I refine the configuration for the app. I don’t want to have a menu with a list of available modules, but it will update its main page every timeToRefresh seconds. Your config will vary, depending on the level of configuration that you need or wish to have.

In the final part I add a list of the modules that this application will have. For each of them I’ll add here its configuration.

The Application class with read and process all this information in its init method.

init: function() {
var self = this;

$.getJSON('conf/app.json')
	.done(function (objData) {
		// this is the info needed to configure an app
		self.arrModules = objData.modules || [];
		self.hasMenu = objData.home.hasMenu || false;

		// setup our globals
		self.arrGlobalParams = [];
		self.arrGlobalParams.appName = objData.appName || '';
		self.arrGlobalParams.environment = objData.environment || '';
		self.arrGlobalParams.language = objData.language || 'en';

		// read our services config
		self.services = [];			
		objData.services.forEach(function (objService) {
			self.services[objService.service] = objService;
		});
})
.fail(function () { alert(‘Configuration file is malformed or missing’); });
}

The last part of the process, once the user has been authenticated (when required to do so) is to launch the Application. At this stage we can load the rest of our libraries, load the language file and load and show our first module or the menu, if we’ve defined so in the conf.json file.

launch: function() {
var self = this;

require([‘modules', 'widgets'],
         function() {
	        // the menu for the application
		if(self.hasMenu) {
			self.menuPage = new ModulesMenu();
			$('body').append(self.menuPage.getContainer());
		}

		// fill the menu and load the first module.
		self.arrModules.forEach(function(objModule) {
			if(self.hasMenu) {
				self.menuPage.add(objModule);
			}
			if(!self.hasMenu && objModule.first) {
				self.doModule(objModule.name);
			}
		});

		// go to menu
		if(self.hasMenu) {
			$.mobile.changePage(self.menuPage.get());
		}
	});
}

And how do we handle our modules? There’s a lot of literature about how to do it, using the AMD format, CommonJS format or other propietary formats. As an introduction, you can read what RequireJS has to say about modules here.