Introducing CanJS

CanJS is the MVC parts of JavaScriptMVC, extracted into a single script download, with additional features that make it sizzle. There’s a lot of client-side MVC libraries out there, but this article will explain what sets CanJS apart from its competition.

posted in open-source, canjs, Development on April 09, 2012 by Brian Moschel

This past Tuesday, at JSConf in Pheonix, Bitovi released CanJS (download). CanJS is the MVC parts of JavaScriptMVC, extracted into a single script download, with additional features that make it sizzle. There’s a lot of client-side MVC libraries out there, but this article will explain what sets CanJS apart from its competition.

Overview

CanJS is a client-side MVC library featuring live binding templates, routes, integration with five major JS libraries, blazing performance, and a tiny size (8.5KB). It packages:

It also includes a rich set of supported extensions and plugins.

What’s Inside

With CanJS’ positive, empowering API, you can.Control your code, you can.Construct your objects, and you can.Model your services! :) Below is a short overview of each major component.

can.Construct

can.Construct is a basic constructor helper used to create objects with shared properties.

var Person = can.Construct({
    init : function (name) {
        this.name = name;
    }
});

var curtis = new Person("Curtis");
curtis.name; // Curtis

var Alien = Person({
  phoneHome: function(){...}
})

It sets up the prototype chain so subclasses can be further extended and sub-classed. can.Model and can.Control inherit from it.

can.Observe

can.Observe provides the observable pattern for JavaScript Objects (and lists).

var person = new can.Observe({ name: "josh"});

person.bind("name", function(ev, newVal, oldVal){
  newVal; // "Josh Dean"
  oldVal; // "josh"
});

person.attr("name"); // "josh"
person.name; // "josh"
person.attr("name","Josh Dean");

You can set and remove property values on objects, listen for property changes, and work with nested properties. can.Observe is used by both can.Model and can.route.

can.Model

can.Model hooks up observables to your back end with declarative service bindings.

var Todo = can.Model({
  findAll : '/todo',
  findOne : '/todo/{id}',
  destroy : 'POST /todo/destroy/{id}',
  update  : 'POST /todo/{id}',
  create  : '/todo'
},{});

Todo.findOne({id: 5}, function( todo ) {
  todo.attr('name') 
});

can.view

can.view is used to load, render, and create HTMLElements from JavaScript templates.

can.view('path/to/view.ejs', {
  message : 'Hello World'
}) //-> fragment <h1>Hello World</h1>

CanJS comes with the Embedded JS template engine built in, but you can use any engine you prefer.

can.EJS

can.EJS (Embedded JS) is a JS templating engine that looks like ERB.

// in devs.ejs
<% if( devs ) { %>
  <% for( var i = 0; i < devs.length; i++ ) { %>
    <li><%= arr[i] %></li>
  <% } %>
<% } else { %>
  <li>No Developers</li>
<% } %>

// render the template
can.view('devs.ejs',{devs: ['Andy','Fred']}) //-> fragment <li>Andy</li><li>Fred</li>

can.EJS provides automatic live binding when used with can.Observes, like in the following example:

// devs.ejs
<% if( devs.attr('length') ) { %>
  <% list(devs, function(dev){ %>
    <li><%= dev.attr(‘name’) %></li>
  <% }) %>
<% } else { %>
  <li>No Developers</li>
<% } %>

// create an observable list and render the template
var devs = new can.Observe.List([
  {name : 'Andy'}, {name : 'Fred'}
])

can.view('devs.ejs',{devs : devs}) //-> fragment <li>Andy</li><li>Fred</li>

EJS intelligently checks for use of the attr method. It binds to changes on the attributes it finds used in any template. When that attribute is changed, it updates the relevant part of the template. For example:

// change the observable data
devs.push({name: ‘Brian’});

When the length property changes the template’s event handler fires and adds an LI to the list. Hot!

can.Control

can.Control is a widget factory used to organize event handlers and create stateful UI controls.

var Tabs = can.Control({
  init: function( el ) {
    // show first tab
  },
  'li  click': function( el, ev ) {
    // hide other tabs
    // show selected tab
  }
});

new Tabs('#tabs');

It can also be used with can.route to organize higher order business rules.

can.route

can.route a special can.Observe that updates window.location.hash when its properties change and updates its properties when window.location.hash changes.

var Routing = can.Control({
  ':type/:id route': function( data ) {

  }
})

new Routing( document.body );
can.route.attr( { type : 'todos', id: 5 } )

It allows single page applications to provide pretty urls and easy back button support.

Why Use CanJS

There are a lot of MVC libraries out there. CanJS sets itself apart with:

Features vs Size

On top of jQuery, CanJS is 8.5k. Here are some other MVC libraries for comparison (compressed and gzipped):

  • Backbone 8.97kb (with Underscore.js)
  • Angular 24kb
  • Knockout 13kb
  • Ember 37kb
  • Batman 15kb

To be fair, size is deceptive, as each library has a different set of features. However, CanJS provides everything you need to create a rich client-side app, at the lowest size of any comparable library. By comparison, Backbone comes with micro templates in underscore.js, but these don’t compare to the power of EJS, so most Backbone apps also include another templating engine that adds to the 8.97kb total library size. We think CanJS hits that sweet spot of small size and rich features.

Ease of Use

We set out to give CanJS the easiest learning curve of any library by documenting the crap out of it. Get your feet wet with the overview page, then dive deeper by reading up on every method and class in the docs page. See how apps are constructed by browsing the example apps, read through the annotated source, and go through the library tests. Read CanJS articles on the blog, ask us questions on the forums, twitter, or get premium support, training, or consulting.

Memory Leak Prevention - Avoid the Zombie Apololypse

CanJS prevents memory leaks you probably don’t even know you have. JavaScript applications commonly leak memory from two sources: event handlers and unused data objects. Explaining this problem is a longer discussion and is covered in its own post.

Suffice to say this is a critical problem for client-side MVC. CanJS handles these leaks for developers automatically, making it almost impossible to create a leaking app.

Performance

Can is optimized for performance in key areas. can.Control pre-processes event handlers to make binding and initialization super fast. Compare initializing can.Control, Backbone.View controls and Ember.View:

Live binding is optimized for performance by directly changing exactly what needs to be updated, rather than the entire template (the nodeValue of a single node, a single attribute value, or a fragment of a template). Compare live binding performance with two other common MVC frameworks:

Library Support

CanJS integrates with five of the most commonly used DOM libraries:

Can’s core utility API maps to whichever library is loaded. This gives you the flexibility to choose your favorite library or even easily switch libraries without needing to rewrite the MVC layer of your app.

There is deep integration with each library, using the element data utility if it exists, deferred objects if they’re present, the library’s synthetic/widget event system (allowing easy integration between can.Control and widgets written for each library), etc. If the library doesn’t support a certain API, Can uses its own.

Conclusion and FAQs

Yes you CanJS! (couldn’t resist) Now go build something awesome.

To answer a couple FAQs that keep coming up:

How does CanJS fit in with JavaScriptMVC?

CanJS is the MVC parts of JavaScriptMVC, with new features like live binding, some polish, and a slightly different API. In that sense, its not really a new library, it has 3+ years experience and hardening under its belt in many large applications.

JavaScriptMVC 3.3 will contain CanJS at its core, but will map the existing JMVC API to CanJS and its plugins, to make updating existing JMVC apps easy.

JavaScriptMVC 4.0 (which will be renamed DoneJS), will change the API of its core components to match the CanJS API.

How does CanJS compare with Backbone?

Read the Why Use CanJS section above, or the Why section on the homepage. A direct comparison article will be coming shortly.

What’s next for CanJS?

A standalone version that works without jQuery or any DOM library.

comments powered by Disqus
Contact Us
(312) 620-0386 | contact@bitovi.com
 or cancel