Ractive.js Expressions and the New Wave of Reactive Programming

by rich_harris on August 19, 2013

The modern web is always changing, and this article is more than two years old.

Ractive_Header

By Rich Harris

Dedicated followers of JavaScript fashion will by now have noticed this season’s hot new trend. If you haven’t spotted it yet, here are a few projects sporting this style on the GitHub catwalk – React, Reactive.js, component/reactive and reactive.coffee.

That’s right: reactive programming is the new black.

At a high level, the idea behind reactive programming is that changes in state propagate throughout a system. Put crudely, this means that in a reactive system where a = b * 2, whenever the value of b changes, the value of a will also change, rather than forever being equal to whatever b * 2 was at the time of the statement.

When we take this idea and apply it to user interfaces, we eliminate the DOM manipulation drudgery that dominates web developers’ lives.

Ractive.js is a new library initially developed to create interactive (hence the name – not to be confused with Reactive.js!) news applications at theguardian.com. It is designed to dramatically reduce the effort involved in creating web apps by embracing these principles.

Let’s look at a simple example:

// We create a new ractive, which renders the following to a container element:
// <p>Hello, Dave! You have 4 tasks remaining.</p>

var ractive = new Ractive({
  el: container,
  template: '<p>Hello, {{name}}! You have {{tasks.incomplete}} tasks remaining.</p>',
  data: {
    user: { name: 'Dave', tasks: { incomplete: 4, total: 11 } }
  }
});

// Later we get some new data:
ractive.set( 'tasks', { incomplete: 5, total: 12 });

// The ractive reacts accordingly, surgically updating the part of the DOM that is
// now out of date:
// <p>Hello, Dave! You have 5 tasks remaining.</p>

Rather than doing any kind of polling or brute-force ‘dirty checking,’ this uses an elegant dependency tracking system: the text node containing the number of incomplete tasks depends on the tasks.incomplete keypath, which is a child of the tasks keypath. So when we update tasks, we know that we need to check to see if tasks.incomplete has changed – but we don’t need to bother checking tasks.total, because nothing depends on that keypath.

As applications grow in complexity, this means much less work for the developer. You might think it sounds like more work for the browser, but it’s not. The non-reactive way to do interactive UI typically involves re-rendering views regardless of whether they’ve changed, and replacing chunks of perfectly good DOM (why hello, garbage collector), which is typically much less efficient.

In other words, reactive UI is a win-win – better for performance, and better for your sanity.

The secret sauce: expressions

This article won’t go any further into the basics of what Ractive does or why we built it – if you’re interested, you can follow the interactive tutorials or read the introductory blog post. Instead, we’re going to focus on one of the features that helps Ractive stand out from its peers, namely expressions.

Expressions allow you to take the logic that only your interface cares about, and put it in your template where it belongs. Yes, I just said that! If you’ve ever had to debug badly written PHP (for example), you may well shudder at the suggestion that logic belongs in templates. But while it’s true that business logic doesn’t belong in your templates, it’s equally true that a lot of presentation logic – aka ‘data massaging’ – doesn’t really belong in your code.

(If you still need convincing, here’s a couple of good articles on the subject: The Case Against Logic-less Templates and Cult of Logic-Less Templates.)

Let’s take our initial example and turn it into a basic todo app along the lines of TodoMVC. Our template looks like this – ignore the question marks for now:

<p>Hello, {{name}}! You have ??? tasks remaining.</p>

<ul>
{{#tasks :i}}
  <li class='task'>{{i}}: {{description}}</li>
{{/tasks}}
</ul>

Meanwhile our model, if you want to use MVC terminology, is a simple array of objects representing tasks:

tasks = [
  { completed: true,  description: 'Add a task' },
  { completed: false, description: 'Add some more tasks' }.
  { completed: false, description: 'Solve P = NP' }
];

ractive = new Ractive({
  el: container,
  template: template,
  data: { name: 'Dave', tasks: tasks }
});

This renders the following:

<p>Hello, Dave! You have ??? tasks remaining.</p>

<ul>
  <li class='task'>0: Add a task</li>
  <li class='task'>1: Add some more tasks</li>
  <li class='task'>2: Solve P = NP</li>
</ul>

This time, there’s no tasks.incomplete property, because tasks is an array. We’ll come back to that. The first job is to rejig the numbers so that it starts at 1, because lists starting with 0 only make sense to programmers. Doing so is trivial:

<li class='task'>{{i+1}}: {{description}}</li>

Next, let’s add a complete class to any completed task:

<li class='task {{ completed ? "complete" : "pending" }}'>{{i+1}}: {{description}}</li>

Now, our rendered task list looks like this:

<p>Hello, Dave! You have ??? tasks remaining.</p>

<ul>
  <li class='task complete'>1: Add a task</li>
  <li class='task pending'>2: Add some more tasks</li>
  <li class='task pending'>3: Solve P = NP</li>
</ul>

Now, let’s deal with those question marks. One way – the traditional way – would be to keep track of the incomplete count as a separate value in our model (or viewmodel, depending on which tribe you belong to), and update it every time the task list changed. The trouble with that is you have to add the necessary logic to every bit of your app that can change the model in some way – a toggle on each task, a ‘mark all as complete’ button, the code that reads from the server (or local storage), or whatever else gets added in future. It doesn’t scale.

A better way is to have the template react to changes by calling any necessary logic when it needs to:

<p>Hello, Dave! You have {{ tasks.filter( incomplete ).length }} tasks remaining.</p>

Then, we just need to add an incomplete filter to our model:

ractive = new Ractive({
  el: container,
  template: template,
  data: {
    name: 'Dave',
    tasks: tasks,
    incomplete: function ( item ) {
      return !item.completed;
    }
  }
});

Now, whenever tasks changes – whether because we’ve added a new one, or changed the status of one or more tasks, or whatever – the expression will be re-evaluated. If the number of incomplete tasks has changed, the DOM will be updated.

As our app becomes more complex, this approach scales beautifully, saving us from a convoluted observing/massaging/updating of our data. You can see a fully fleshed out TodoMVC implementation here – (the source code is possibly the shortest of any implementation, and arguably some of the most readable).

It also allows us to do things like sophisticated animations, without reams of complex render logic.

How does it work?

Traditional templating engines work by interpolating strings, the result of which is typically rendered using innerHTML. Ractive is different – it parses templates into a tree-like JSON structure which contains the DNA of the app, using a PEG-style parser. When it encounters an expression, the parser first creates an abstract syntax tree representation of it, then extracts the references from the AST, then collapses it down to a string representation.

Ractive.parse( '{{i+1}}' );

// results in the following - it's deliberately terse, so that
// you can parse on the server and send the result to browsers
// without wasting bytes:
// {
//   t: 2,          // the type of mustache
//   x: {           // the expression
//     r: [ "i" ],  // the references used in the expression
//     s: "${0}+1"  // a reference-agnostic string representation
//   }
// }

Later, when Ractive renders this mustache, it will try to resolve the references used in the expression. In this example, there’s only one reference – i – which resolves to 0 for the first task, then 1, then 2 and so on. Ractive creates a function – using the Function constructor – that takes the value of i as an argument and returns i+1.

This might seem like a roundabout way to add 1 to something. But because we only need to create the function once, rather than repeatedly evaling code (which is slow), it’s a memory-efficient and performant way to solve the problem even when dealing with hundreds of tasks.

And because we know which references are involved, we know when to call the function again. Consider the next expression:

Ractive.parse( '{{ completed ? "complete" : "pending" }}' );

// {
//   t: 2,
//   x: {
//     r: [ "completed" ],
//     s: "${0}?'complete':'pending'"
//   }
// }

For the first task, the completed reference resolves to the keypath tasks.0.completed – for the second, tasks.1.completed and so on. In each case, Ractive registers the mustache as a dependant of the keypath, so that when tasks.0.completed changes (again, it doesn’t matter what happened to cause it to change – that’s the beauty of reactive programming), the expression is re-evaluated and the DOM is updated.

So is it ‘real JavaScript’?

Up to a point. Since in a reactive system we don’t have control over when the expression is evaluated, it’s important that expressions don’t have side effects – so if we try to use assignment operators such as foo = bar or foo += 1, the parser will fail. The same goes for certain keywords, such as new, delete and function.

Of course, it’s still possible to create side-effects by referencing a function which itself has side-effects. Remember our incomplete filter? There’s nothing to stop you doing this:

ractive = new Ractive({
  el: container,
  template: template,
  data: {
    name: 'Dave',
    tasks: tasks,
    incomplete: function ( item ) {
      doSomeExpensiveComputation();
      counter += 1;
      return !item.completed;
    }
  }
});

But by parsing expressions and blocking ‘accidental’ side-effects, we can encourage best practices without preventing power users from manipulating the system to their own ends.

The future

Expect to see more examples of the reactive programming trend coming to a repository near you. As with all programming paradigms, it’s not a silver bullet, but by integrating reactive thinking into our work we can start to structure our applications in a way that is more readable and (often) more efficient.

Ractive.js is under active development – it’s production-ready (to get started, try the 60 second setup or follow the interactive tutorials), but will continue to evolve as we collectively figure out the secrets of reactive programming. If you’re interested in being part of that conversation, come on over to GitHub and help shape the future of web development.

10 comments"

  1. Pete A says:

    Interesting article, thank you. This looks as if it’s geared towards a ‘code first’ approach with HTML templating contained within the JS; an alternate approach that we’ve been having a lot of success with is AngularJS (http://angularjs.org) which takes more of an ‘HTML first’ approach with bindings (which are reactive to changes, similarly to ractive). Could you please offer some insight as to how you’d compare & contrast these two projects and why you favour the ractive approach?

    1. Ben Tesser says:

      AngularJS uses what the author refers to as “brute force ‘dirty checking'”. This means that whenever any variable is updated, angular goes through every value to determine if it has changed, and if it has, it will update. This approach requires manual activation of any section that needs updating, but does not have to check other areas.

      I find Angular’s approach works very well, except when there are labor intensive $watch statements or $filters. It takes a lot of the boilerplate out of events and updating. Both approaches are useful, depending upon the specific application.

      PS
      One way to get around the slow filter operations is by filtering in a service or the controller before the data gets to the view.

    2. Rich Harris says:

      Ractive definitely takes a lot of inspiration from Angular (in a previous incarnation it was called Anglebars, because it was half way between Handlebars and Angular – even its logo was a ripoff! http://i.imgur.com/Wq369yM.png).

      I get asked this question a lot – at some point I should probably do a proper comparison between Ractive and Angular (and Ember, and React, and Knockout, and so on). They attack overlapping problem spaces, but there’s a philosophical difference – whereas with Angular you build your app within the framework, Ractive lets you create interactive components within your app. Personally I’m not a big fan of MV* ‘frameworks’ (as opposed to libraries) because I often discover too late in a project that I can’t quite bend them to my will – my app is a slave to the framework I’ve chosen rather than the other way around. (Not that this necessarily applies to Angular – a huge number of developers are very productive with it, including my colleagues, and in any case the framework/library dichotomy is fuzzy at best.)

      Technical differences: Angular isn’t ‘truly’ reactive, in the sense that it doesn’t have a concept of changes propagating through a system. One of the developers, Misko, wrote an illuminating post on this subject on Stack Overflow: http://stackoverflow.com/questions/9682092/databinding-in-angularjs/9693933#9693933 – he says that dependency tracking is ‘a clever feature for a problem which Angular does not have’ (never mind that Angular’s event loop is a clever feature for a problem that no other library has!). There are definitely pros and cons to each approach (dependency tracking vs dirty checking), but Ractive favours dependency tracking because it allows complex computed values and puts more control in the developer’s hands.

      1. Shamma says:

        @Rich Harris: Really thanks to this amazing work! :’)

        I… I was exactly watching for how to do “handlebars reactive programming”, because I thought that meteor was “brute force ‘dirty checking’ ” too (it refresh all the ” block when there is even only one change in it…)

        So, 15h I was on it, actually it is 2 am… And now I read that : “This kind of **surgical** DOM manipulation is more efficient than constantly trashing views only to re-render them, and it scales far more elegantly than manually updating elements.” and this ” In this example, Ractive.js constructs a parallel DOM representation which is aware of its dependencies on the values of user and messages.unread. **When those values change, it knows exactly which parts of the real DOM need to be updated**.”

        He get it! This was marvelous…
        Thanks a lot 😉

        (**surgical**… I definitively love this world! And it describe very well your purpose…)

      2. Troy says:

        @Rich Harris, thanks for writing Ractive, it looks very cool. I’ve always thought the angular guys hacked the loop together as a bridge to the future until Object.observe arrives.

        I like how Ractive handles this, calling .set(‘keypath’) seems logical. I use angular a lot and the digest loop provides no real benefit, but is required because their is no other way to tell angular what data has changed.

        So I’m off to play with your awesome creation a bit, in parting for now I have yet a simple request: Please stay lean, or modular! I noticed the non-runtime Ractive is close to the size of angular. Would be cool if Ractive was no bigger than rivetsjs 😉

  2. DjebbZ says:

    Another question comparing Angular and Ractive. I like both projects a lot. Dependency-tracking vs dirty-checking, I’m not arguing which one is better. In one case one has to worry about side-effects, in the other case one has to worry about performance. What I do like in Angular though is that you it helps the developer manage a single source of truth for data, so that wherever it changes, it changes for the whole application (UI, other sub-systems, local-storage, whatever). Ractive doesn’t provide this, so how do you manage this problem ?

    1. Rich Harris says:

      It’s a very good question. Ractive doesn’t consider itself an MV* framework (see my answer above), so each instance is responsible for managing its own state, and these can easily become decoupled from other instances or the app as a whole.

      There are two approaches being worked on to solve this problem with apps built with Ractive. The first is something called ‘adaptors’ – the idea is that a Ractive instance can ‘bind’ to a model via an adaptor. So you might have a Backbone Model/Collection adaptor, or a Firebase adaptor, or CouchDB, or whatever is responsible for providing that single source of truth (SSOT). I’m working on a state management library called Statesman (https://github.com/Rich-Harris/Statesman) – it’s immature and the docs are out of date (!) but it also has an adaptor and is built on similar reactive principles to Ractive. Within my own apps, Statesman usually provides the SSOT.

      The second is components. These are inspired by Web Components, and similarly to Angular directives they allow you to encapsulate markup and functionality in a module that can be nested within a larger app. This allows state changes to propagate from parent Ractive instance to child instance, and vice versa. There’s a discussion about this feature on GitHub – https://github.com/Rich-Harris/Ractive/issues/74.

      Neither of these features are quite ready for prime time, but watch this space!

      1. Troy says:

        The angular SSOT is flawed IMHO. It depends on $scope inheritance, which requires the developer to know exactly which directives mess with the inheritance chain. I’ll just say it now, I love angular and use it everyday, but $scope inheritance and it’s digest-cycle (dirty-tracking) are horrible design decisions (don’t mean to be brutal, hind-sight is always 20/20)

        What I like about Ractive is I can make a reliable SSOT because Ractive will not magically do this for me, and it’s not that hard. In fact, any moderate sized angular app is best served by putting data into a service (singleton for the app) and that’s what I would do with Ractive as well. I would have Ractive check data in and out of a singleton as a best practice and not a “framework feature”

  3. Joel says:

    This looks like the next best option to Elm-lang (which doesn’t look primetime yet).

    1) Is Ractive.js ready to build a complex app? (A full single page application)
    2) Where is the mailing list for Ractive.js?
    3) How does this compare to Facebook Reactive.js (is that one even FRP)?
    4) Anyone working on a set of prebuilt components?

    J

Leave a Reply

Your email address will not be published. Required fields are marked *

© 2016 Modern Web & our authors. All rights reserved.