Riot 1.0

IMPORTANT This version is no longer maintained

Riot 1.0 introduction

Riot is the most minimal approach to MVC world. It weighs 1kb and has only 3 public methods so it’s extremely simple and easy to learn. A Riot application uses vanilla JavaScript to structure the code. You’ll use classic design patterns instead of some framework specific idioms.

Riot applications are modular and can be maintained by multiple developers. The frameworkless nature forces you to focus on the most important: your business logic and the API.

For broader introduction see the original blog post about Riot.

What is modular?

Your job is to build applications that are split into modules. Each module performs a logically discrete function and it’s not dependent on other modules. This has major benefits:

  1. A large program can be broken into smaller and simpler units
  2. Modules can be added/removed/modified without affecting the other parts of the application
  3. Several programmers can work on individual modules at the same time
  4. The program structure is easy to understand even for newcomers
  5. Ability to build a different subset of modules for different needs

Modularity is the single most important key for large scale applications. Basically a modular application consists of two things:

  1. Core
  2. Modules

The application core is a piece of software with a well documented API. It’s not just a data layer, that you might have seen on other MVC setups – it’s your precious business logic.

The modules are extensions to the core. These modules are “loosely coupled” meaning that they hook to the application core by listening to it. The core is not aware of these modules.

There are no hard-coded function references because strong coupling does not scale. There can be hard-coded dependencies inside the core, but not between the core and modules.

To actually keep things modular means constant organising work. Things should be on their most logical place. Think layers in Photoshop, table of contents on a book, or visual hierarchy in a user interface. It’s constant balancing. And when you add features it must fit the big picture.

Model, View and Presenter

When talking about modularity in single-page applications it’s better to clean the table.

First you need to know the goals of your application. What does and what doesn’t it do? You model your business with JavaScript. That’s the application core. Also called Model.

Then you have the browser and the user interface. The layout (HTML), document object model (DOM) and the style (CSS). The piece that the user sees and interacts with. That’s the View.

Then you need to bind these two things together. You need to listen to what happens on the View: clicks, keypresses and scrolls. You also need to listen to the Model: maybe a new entry came trough a real-time channel, or someone liked a post. This someone listens to all these events and reacts accordingly.

This “middleman” is called the Presenter.

Riot uses these terms to describe the big picture: Model, View and Presenter.

MVP at its heart aims to separate the application logic (Model) from the user interface (View). This separation is important because it simplifies your code and makes it more testable. The lack of this high level separation causes so called “spaghetti code” where business and UI logic are mixed together. This is the jQuery era before single page applications (SPA) started gaining popularity.

In classic UI terminology Riot uses the Passive View (1) branch of the MVP family.

A Passive View handles this by reducing the behavior of the UI components to the absolute minimum by using a controller that not just handles responses to user events, but also does all the updating of the view.

Unlike most MVC-style configurations, Passive View results in no dependencies between View and Model. It’s a simple pattern and easy to understand – which ultimately gives you more power.

Lastly, it’s important to realize that MVP (or MVC) is not a framework. It’s a high level design pattern. Its purpose is to simplify the architecture of UI heavy applications. It’s simply a way to structure your code. In loosely coupled application the modules communicate with each other with events.

Most of the current frameworks are an overkill since basically all you need is a proper event system.

Model

Model is the application core. It’s the most important part of your application, since everything is built on top of it. It’s the public interface to the rest of the world. You’ll be using it, your team will be using it, and 3rd parties are using it.

A well designed application core can be extended with loosely coupled modules and allows anyone to develop the system on their own.

In Riot the model is a complete application and not just a helper object for the presenter layer as you might have seen on MVC configurations. It’s also a good practice to keep your Models fat since the closer you come to the view the harder it becomes to test the assets. Anything that is difficult to test should have minimal behavior.

This allows multiple different user interfaces to be built on top of the same Model. Think different Twitter clients for example (back when things were fine with them). People could develop wildly different experiences on top of the same API without knowing about each other.

Designing the Model

Model is the starting point of the application design (View is another, depending on preference). You should reserve time for designing the Model and make it as simple as possible because you’ll be spending lot of time with it later. Set the bar high. Think jQuery API for example.

Two things to keep in mind:

  1. What problem does the product solve?
  2. Who is using the product?

Model is a domain specific thing. Think what your application does, what are the goals, what features are there, and what features are not there. Breaking your business into logical pieces and thinking how they communicate. Something you may have done with object oriented languages in the past.

Keep these in mind when designing the properties, methods and events. Not going to go deeper here, but the API is the root of all good or evil.

Backend

Riot does not include a separate backend component. This is by design. REST dominates the current way of thinking, but Web Sockets and realtime patterns are just around the corner, where RPC-style protocols make more sense.

Your backend interface can just have a generic call method so that the underlying implementation can change. Be it REST, RPC, or a custom AJAX based thing.

View

The view is what the user sees and interacts with. The HTML page on a web browser. What’s actually interesting for a JavaScript developer is the document object model (DOM). You can do all kinds of creative tricks to build user experiences. Most importantly it’s a source of events:

  1. User events: click, scroll, keypress, mousemove etc.
  2. Document’s ready event
  3. URL change events

These events are in special interest for Presenters, which perform the actual manipulation of the view. The view itself has no logic – just plain old HTML and CSS code.

Presenter

Presenter is the important middleman between the View and Model. Each presenter is a loosely-coupled module performing a discrete function and can be developed individually.

Presenter listens to the View events and calls the appropriate methods on the API. It also listens to the Model events and manipulates the DOM accordingly.

Presenter doesn’t make any assertions about the business model.

Note that other frameworks may call this layer a “view”, but in MVP this is called the “presenter”.

Templating

Sometimes I like to start developing a single-page application with just HTML and CSS only. It’s amazing how much you can do with those. You can fine tune the design and user interface to a nearly complete state. By adding simple CSS class name switches with JavaScript you can even make those beautifully animated view switches. In reality, of course you cannot keep your hands out of JavaScript but it’s totally possible to show the customer a complete application UI without single line of the actual logic.

The best thing is that you can naturally continue from that HTML view to complete the whole application. Ideally you have also completed the API so you only need to write the presenters to hook these two together.

This HTML mockup is your collection of templates. Before starting with the presenters you can move some of the HTML inside <template/> or <script type='text/template'/> tags, typically the ones that are rendered multiple times inside loops.

No logic

Riot takes a strong position on not recommending logic inside the templates. There are multiple reasons for this:

  1. Pure HTML is cleaner and passes W3C validator. A HTML mixed with a template syntax is messy.

  2. HTML is not inherently meant to describe logic

  3. Template loops are unnecessary in realtime applications where the iterateable lists change over time. This must be the hardest challenge for the current template languages.

  4. Logic inside HTML is hard or impossible to test

  5. Logicless templates are sometimes an order of a magnitude faster, especially on non-webkit browsers (2)

  6. In complex loops it’s natural to leave the data calculation logic for JavaScript and keep the templates clean.

Look for the logic on the customer listing. The single template is rendered multiple times on a loop and the width property is calculated with JavaScript and the template remains simple.

Routing

The demo application performs routing on the presenter layer as follows:

// 1. Links
app.root.on("click", "[href^='#/']", function(e) {
  riot.route($(this).attr("href"))
})


// 2. Routing (Mapping between URL and API method)
riot.route(function(path) {
  app.load(path.slice(2))
})


// 3. UI logic
app.on("before:load", function() {

  // remove existing class
  $(".page.is-active").removeClass("is-active")

}).on("load", function(view) {

  // set a new one
  $("#" + view.type + "-page").addClass("is-active")

})

Here’s how it works:

  1. Links: Select links that perform view switching and the change of the URL. We use jQuery’s delegated events to lazily pick all anchor links from the page, even if they are dynamically created in the future.

  2. Routing: Defines the API method to be called when URL changes. The demo application API has only one, generic load method that takes a page name as the argument, but you can have more complex mapping between URL’s and methods.

  3. UI logic: Defines what happens in the UI when the API method is called and data (or “page”) is returned from the server. Here we assign an “is-active” CSS class name to do the animated page switch using CSS transitions.

The API emits “before:load” and “load” events so the presenters can implement the page swithing logic inside event handlers. This way you can call the load API method from the console too, and the UI behaves similarly as it would work by clicking the navigational links.

Routing is one of the main things that defines a client-side framework. In Riot the riot.route behaviour is just a thin layer above the API to deal with the back button. The API can focus on the business logic only unaware of the web layer. The routing on the presenter is completely transparent and each of the steps can be customized based on your application needs.

The above code only handles the switch and the actual rendering of the returned view object is dealt by a different, view specific presenter.

jQuery

jQuery exists because the vanilla DOM is a complete disaster to work with. jQuery does a massive cleanup by exposing the DOM for the page developer in an elegant and friendly way.

There are other implementations (3) too, but the real credits go to John Resig. He designed the API, which has become a standard.

Feel free to use any of the implementations, but the advantages of using jQuery itself are:

jQuery is currently found on 58% of all websites and 93.0% of all the websites whose JavaScript library is known (4). These numbers are growing all the time.

jQuery API is a perfect match for Riot. It’s an ultimate tool for building presenters. The jQuery selectors provide an unobtrusive way to bind the model to the view. And there can be multiple presenters dealing with the same set of HTML elements unaware of each other.

Id’s and class names provide a natural mechanism to hook functionality, just like you can hook styling with CSS. Just remember that if the class names and ids change you need to update the presenters too.

Note that Riot is not dependent on jQuery. You can use other DOM libraries too such as DOJO or Mootools or you can go with Facebook React.

Module interface

Now let’s focus on coding. We want a simple way to extend the application core with modules. Here’s a nice riot.observable trick to split the application into loosely-coupled modules:

// make it work on client and server
var global = is_node ? exports : window,
  instance

// only single global variable is exposed
global.admin = riot.observable(function(arg) {

  // when called without argument, the API is returned
  if (!arg) return instance

  // function argument --> bind a new module
  if ($.isFunction(arg)) {
    admin.on("ready", arg)

  // configuration argument --> start the application
  } else {
    instance = new Admin(arg)

    instance.on("ready", function() {
      admin.trigger("ready", instance)
    })

  }

})

The above code exposes a single global variable admin that can be used as follows:

admin(function(api) {
  // module #1 logic, API is given as argument
})

admin(function(api) {
  // module #2 logic
})

All UI functionality on the demo application is wrapped inside modules like this. Now after you have all the code inside the modules you need to launch the application:

admin({ page: location.hash.slice(2), root: $("body") })

This will start the application with the configuration given on the argument and all the modules are initialized when the “ready” event is fired. This is typically called after the DOM has been loaded on the bottom of the page or inside jQuery’s document.ready. The given argument typically contains the initial path of the application, as well as the root element. Your application has its own set of configuration variables, of course.

You can access the API after the initialization as follows:

var api = admin() // get access

api.load("customers") // call an API method, also try with "stats" for example

Try that on the JavaScript console on the demo (must be logged in)! All features that you can do with the UI are also available on the API. This is ensured by the strict separation of API and presenter modules.

Now your application is nicely split into decoupled modules and there is a simple way to add new modules. Each of your team members has only one, global admin method to use and the API is given as the first argument to each module. The modules are isolated and can be freely added / removed without breaking other parts of the application. You can have modules on both the API layer and the presenter layer.

Finally, the module interface is “white labeled”. You can name the crucial module interface after your application and there is no 3rd party framework to force the naming scheme. Much cooler!

Application lifecycle

When the application starts a sequence of events happen. Here is a typical flow:

  1. The call to server is made to load the initial view data
  2. The API is fully constructed
  3. The modules are initialized
  4. A “load” event is fired with the returned initial data

An application that requires authentication needs an extra step to handle unsuccessful logins.

Now let’s look at the code:

// 1. load initial data from server
backend.call("init", conf.page).always(function(data) {

  // 2. construct API, we only create a User object on this demo
  self.user = new User(self, data ? data.user : {}, backend)

  // 3. all ready --> modules are loaded
  self.trigger("ready")

// init was successful
}).done(function(data) {

  // 4. load event
  self.trigger("load", data.view)

// init failed
}).fail(function() {

  // listen once when user logs in
  self.user.one("login", function(data) {
    $.extend(self.user, data.user)

    // 4b. fire a load event after successful login
    self.trigger("load", data.view)

  })

})

Application bootstrapping with user login is actually quite a complex thing to do, but the above flow is quite readable.

API

Riot API is 3 things: an event library, a templating engine and a routing system.

Events

Riot event system is in many ways similar to jQuery but there are 2 significant differences:

1) There is no Event object because it’s not relevant outside the DOM.

2) You can trigger and receive event arguments without wrapping them to an array.

riot.observable(el)

Adds an Observer support for the given object. After this the object is able to trigger and listen to events. For example:

function Car() {

  // Make Car instances observable
  var self = riot.observable(this)

  // listen to "start" event
  self.on("start", function() {
    // engine started
  })
}

// make a new Car instance
var car = new Car()

// trigger "start" event
car.trigger("start")

el.on(events, callback)

Listen to the given space separated list of events and execute the callback each time an event is triggered.

// listen to single event
el.on("start", function() {

})

// listen to multiple events, the event type is given as the argument
el.on("start stop", function(type) {

  // type is either "start" or "stop"

})

el.one(event, callback)

Listen to the given event and execute the callback at most once.

// run the function once, even if "start" is triggered multiple times
el.one("start", function() {

})

el.off(events)

Removes the given space separated list of event listeners

el.off("start stop")

el.off(event, fn)

Removes the given callback from the event array

function doStart() {
  console.info("starting")
}

el.on("start", doStart)

// remove a specific listener
el.off("start", doStart)

el.trigger(event)

Execute all callback functions that listen to the given event

el.trigger("start")

el.trigger(event, arg1 … argN)

Execute all callback functions that listen to the given event. Any number of extra parameters can be provided for the listeners.

// listen to "start" event and expect extra arguments
el.on("start", function(engine_details, is_rainy_day) {

})

// trigger start event with extra parameters
el.trigger("start", { fuel: 89 }, true)

Templating

Riot has an exceptionally fast templating system to take your HTML markup and populating them with application data.

riot.render(template, data)

Take the template and return a string where all tokens (labels wrapped with curly braces) are replaced with the given data. For example

// simple render
riot.render("<h1>Hello {name}</h1>", { name: 'John' })

// render nested objects
riot.render("<h1>Hello {user.name}/h1>", { user: { name: 'John' } })

Both of the above will return <h1>Hello John</h1>. The template string cannot contain any logic due to multiple reasons.

riot.render(template, data, true)

Same as riot.render(template, data) but all strings on the data are escaped as follows:

This prevents any XSS exploits on the data. Use this method if the data is provided by the end users or you don’t oterwise have control over the data. The drawback is that this method is about 15 times slower than the above non-escaping counterpart.

// returns: "<h1>Hello &lt;freak&gt;</h1>"
riot.render("<h1>Hello {username}</h1>", { username: '<freak>' })

riot.render(template, data, fn)

Provide a custom function to transform the given data. For example:

// @- characters not allowed on username
riot.render("<h1>Hello {username}</h1>", { username: '@lolly' }, function(str, key) {
  if (key == 'username') return str.replace("@", "")
})

Routing

riot.route(to)

Changes the browser URL and notifies all the listeners assigned with riot.route(callback) method below.

riot.route("#!/customers")

riot.route(callback)

Execute the given callback when the URL changes (via back button) or when riot.route(to) is called. The function is also called once upon page load – Riot makes sure this happens consistenly across browsers.

riot.route(function(hash) {
  // hash is a shortcut to location.hash
})

See how routing is implemented on our demo application.

Back button is supported on browsers with history.pushState support. This includes all browsers except IE9 and below. On IE9 the feature falls back nicely and back button simply goes to previous page ignoring the changes on location.hash.

Extensions

In the above example the backend.call() method returns a promise interface that is created with following riot.observable trick:

function Promise(fn) {
  var self = riot.observable(this)

  $.each(['done', 'fail', 'always'], function(i, name) {
    self[name] = function(arg) {
      return self[$.isFunction(arg) ? 'on' : 'trigger'](name, arg)
    }

  })

}

This is a generic observable implementation that you can take advantage of in any JavaScript application, not just in MVP configurations.

There is no specific extension mechanism in Riot. I think simple copy/pasteable functions are the best reusable components in JavaScript world. Riot itself is just 3 functions. They all extend the $ namespace since it already exists on most websites (created on demand if it doesn’t exist) and it’s not cool to set global variables.

There is an unfortunate tendency to build unnecessary boilerplate around simple functionality (options, repositories, build systems etc…) while it could all be expressed with just one function. Simple functions are usable anywhere.

I’d love to see something like Gist but with better discoverability (search, tags etc.) A big mass of reusable functions that you can paste to your own project, perhaps modifying them to your needs a bit. Someone do that for us, please!

Conclusion

Riot applications are smaller and they are written with vanilla JavaScript and jQuery. The business logic is completely separated and it has a nice API. All the functionality around the API is on isolated modules that can be developed independently. The API can be run and tested on the server side, and the application is fast because of the simple architecture and the logic free templating.

All the above concepts are applied on a well documented demo application. It’s an administration panel that you can use as a starting point.

This documentation was a high level introduction to modular JavaScript programming but this is an ongoing process. The next update will add more detailed sections about Riot API, testing, building and deploying.

[1] MVP passive view

[2] Riot templating is Fast

[3] jQuery API implementations: Zepto, Minified.js

[4] Usage statistics and marketshare of JQuery for websites