Some say that a web page should start with HTML, which is (or was) thought of as content, and that functionality should load on top of that (the filtering etc). Right or wrong? With so much content now generated dynamically, perhaps it’s time to revisit the way we do website design. Whichever way you lean, the back-end will largely be doing the same as it was ten years ago, but with a bit of partial serving and new protocol support. We’re still left with the problems of old: building and rendering a page made up of multiple fragments and trying not to hit a database server multiple times, which means careful thought on how data passes through the modules that represent them. And front-end wise, there’s the pain of trying to put a state into the /x/y/x
URL being served, even though the user never loaded x
and x/y
to begin with.
I always thought there had to be a better way, but JavaScript, or the variety of devices that ran it, were never really up to shifting the work load from back-end to front-end. Bits yes, but not all of it.
And then they were.
Along came the frameworks; Backbone, still here, but diminished, Polymer, the next big thing — last year, the Angular explosion — now smoldering, and more recently Facebook’s React. I feel something is different with React, something that tells me the web is finally heading in the right direction. For one it isn’t using templates and thus has no need for two way data-binding.
The real architectural decision is not what templating language to use, or whether one should use TypeScript, or use a framework at all, it’s whether anything at all should be rendered server-side. A year ago it was mandatory, because the service that could make or break a product, GoogleBot, couldn’t handle a web app. But now it can, and so I must ask you the question you’ve asked yourself many times over the past several years: “Should I reload that part of the page via Ajax as a fragment, or regenerate that piece with front-end code (a.k.a. React), or modify the DOM structure with front-end code, or just reload the page?”
Then I will ask you one more question. It’s 2015. Why are we still asking this question?
A year earlier I embarked on the road to create a solution.
Introducing the Igaro App JavaScript Framework
Igaro App is a web app framework — a.k.a. an SPA (Single Page Application) framework — but one which takes a radical departure from the norms. For a start it uses no HTML and no MVC. It avoids using DOM query selector methods to try to improve performance, and therefore has no template engine. Instead it uses routes to build pages, and standard CSS for styling and layout.
It is written in pure JavaScript ES3/5/6 (no dependencies) and aims to outclass and outperform not at several things, but at everything. It’s purpose is to prove that by thinking outside the box it is possible to produce an architecturally brilliant foundation on which developers can code using the JavaScript they love, and not some abstracted, string parsed derivative.
Bold ideas and bold claims, and you should be skeptical. When you first view the website for Igaro App you may wonder where the center aligned “Apple like” sales page is, with its big fancy fonts and basic images. It’s inherently different because the website is the web app, which is also the downloadable repository. It’s functional and functioning, with all the documentation and widgets built right in.
Go ahead and clone it right now:
mkdir igaro
git clone https://github.com/igaro/app.git igaro/git
npm install grunt-cli
gem install compass
cd igaro/git
npm install
grunt
Once cloned and running, the user has a development environment ready to go. Igaro compiles into two modes — debug and deploy and a web server for each can be found on ports 3006 and 3007 respectively. These will reload automatically as you work. Modifying “pages”, or routes, is as simple as editing the included route.*
files . To see which route file maps to which page, or to inspect the source code for any page, just click the curly braces in the header section. A XHR widget allows for seamless JSON retrieval and creating new widgets to span over multiple pages can be done by inspecting any of the included instance.*
files.
The Anatomy of Igaro App
Routing
If you’re used to writing HTML and ‘wiring up’ a controller then it’s a little difficult to grasp the idea of doing away with MVC. In Igaro App, you must consider that a URL contains routes or data for them. Root (/
) is the base route, for which the default repo has non-navigable routes — header, main (which becomes the base) and footer. If a user browses to /store
this route may be loaded from file or via an API, as defined by a provider. Either way, JavaScript creates the view, which is embedded into the parent or base route. An example of a basic “hello world” route is shown below.
//# sourceURL=route.main.helloworld.js
module.requires = [
{ name:'route.main.helloworld.css' }
];
module.exports = function() {
"use strict";
return function(route) {
var wrapper = route.wrapper,
domMgr = route.managers.dom;
domMgr.mk('p',wrapper, _tr("Hello World"));
};
};
Here a dependency module (CSS) is loaded prior to the route’s availability. A DOM element is created using a manager on the route so that relations can be kept (destroy the route, destroy the DOM element). When a blessed object (more on what these are in the next section) is destroyed in Igaro, everything related to it is cleaned up — events, DOM elements, child objects and circular references.
Igaro App is set up for multiple locale and _tr
is the default parsing name.
Object Interaction
Igaro App introduces a concept called bless, which takes standard JavaScript objects and decorates them with attributes and helpers. One of the more important of these is a circular reference to a parent object, which in turn means events can be propagated (worry not, a destructor event cleans these circular references automatically). The net result is that objects don’t need to be duplicated and extended to inherit their parent’s properties, and thus memory consumption and performance is significantly improved.
Another feature of bless is the decoration with managers. Standard managers are ‘dom’ (which allows DOM elements to be linked to the object and removed automatically), ‘debug’, and ‘events’, while optional managers such as ‘store’ allow any object to save/retrieve data locally/remotely.
It’s worth mentioning at this point that most functions in Igaro return a Promise and that the entire codebase is callback free. Code flows in a readable manor that works to document itself. JSDoc is not used here, instead every function and its arguments are documented in the manual built into the app.
Plugins
Igaro App comes with a library of plugins which may include widgets for a page, and which may be loaded on demand from a file server or API. Plugins are bog standard JavaScript OO, with bless wiring them together. The library currently counts XHR, storage, RTE (rich text data entry), accordion, slideshow, validation, toasts (popup notifications), page messages, modal dialogs, oauth2, navigation and JSONP among it’s inventory. The example below shows how to add a hideable page widget to a route.
route.managers.object.create('pagemessage',{
container:wrapper,
type:'info',
message: _tr("Hide me and I'll not return."),
hideable: true,
id:route.path.join('.')+'.test01'
})
Security & Authentication
Igaro App has no public variables and doesn’t load itself onto the window object, hence it can’t be accessed by other scripts.
Within the included widgets set is an Oauth2 module which supports transparent authentication without URL switching or redirection. Example code to do this for Google Oauth2 is included in the documentation. An XHR call to the API needn’t handle a 401 as it will be picked up by the Oauth2 module, handled, and the request continued once credentials are supplied. You can do this in Angular by injecting middleware. In Igaro App there’s no middleware, everything is done with events. The example below shows how a request to a Google API is made.
return route.managers.object.create('xhr',{
stash: { auth:'google' }
}).get({ res:'https://www.googleapis.com/drive/v2/files' }).then(function (data) {
// data here
});
The Promise catch method is only used for bespoke error handling, otherwise returning the Promise will allow errors to be handled at base level.
Build & Deploy
Grunt is used for the workflow, and (by default) compiles Igaro App into two modes: debug and deploy. Differences include JSMinify and the way errors are handled. In Igaro App, all errors are handled and are reported to the user in a meaningful way. For deploy mode, errors can be sent to a remote server for further analysis. Real-time compilation for both modes is automatic.
Testing
A test suite for all pages supplied within the web app is provided. Given any error will invoke a standard response, it’s easy to test that a page in its entirety has loaded without error. Unit tests for Igaro App’s modules are ongoing and need further coverage, although no errors are known at time of writing. The web app, as provided, lacks DOM event tests but given Mocha, Nightwatch and Selenium driver are used it wouldn’t be difficult to include your own once you create your own app.
Summary
Igaro App may be the breath of fresh air you’ve been looking for. It may not be sponsored by Google or Facebook, and it may not work well with your existing code base, but it does provide a sterling architecture on which to build your next one.
In a future article I’ll be offering a more technical insight into the framework by creating a todomvc widget using standard OO JavaScript and Igaro App’s bless.
In the meantime, clone the repo and give Igaro App a whirl. Enjoy!
Andrew Charnley is a JavaScript Developer & System Architect contracting out around the world. A keen traveller, he typically cycles from role to role.