Welcome to CanJS!
CanJS is a MIT-licensed, client-side, JavaScript framework that makes building rich web applications easy. Use it because it’s:
CanJS provides:
Get Canned
CanJS’s core supports jQuery, Zepto, Dojo, YUI and Mootools. Select your core download below or select the individual plugins above and click download:
- can.jquery.js (min)
- can.zepto.js (min)
- can.dojo.js (min)
- can.mootools.js (min)
- can.yui.js (min)
The Using CanJS section details the minor differences among use with other libraries.
Learn
Our goal is to make learning CanJS as easy as possible. There are a number of resources to use:
This Page - Walks through the basics of CanJS by building the following small todo app with CanJS and jQuery:
Click a todo to edit it. Use the browser’s forward and back buttons to change what todo is being edited.
Getting Started With CanJS Video - An in-depth walkthrough of CanJS. If you have 42 minuites, you will learn CanJS and how the todo app was built.
In Depth Documentation - Want to know the deepest details of CanJS’s API?
Recipes - Checkout some nifty functionality built with CanJS.
Example Apps - How CanJS has been put to good use.
Annotated Source - for jQuery, Zepto, Dojo, MooTools, YUI
Test Suite - See how we poke around the API.
can.Construct can.Construct( [classProperties,] [prototypeProperties] )
Constructor functions made with can.Construct are used to create objects with shared properties. It’s used by both can.Control and can.Model.
To create a constructor function of your own, call can.Construct with the:
- classProperties that are attached directly to the constructor, and
- instance prototypeProperties.
can.Construct sets up the prototype chain so subclasses can be further extended and sub-classed as far as you like:
var Todo = can.Construct({
init: function(){},
author: function() { ... },
coordinates: function() { ... },
allowedToEdit: function( account ) {
return true;
}
});
var PrivateTodo = Todo({
allowedToEdit: function( account ) {
return account.owns( this );
}
});
If only one set of properties is passed to can.Construct, it’s assumed to be the prototype properties.
init new can.Construct( [args ...] )
When a constructor is called with the new keyword, can.Construct creates the instance and calls can.Construct.prototype.init with the arguments passed to new Constructor(...).
var Todo = can.Construct({
init: function( text ) {
this.text = text
},
read: function() {
console.log( this.text );
}
})
var todo = new Todo( 'Hello World' );
todo.read()
can.Observe new can.Observe( data )
can.Observe provides the observable pattern for JavaScript Objects. It lets you:
- Set and remove property values on objects.
- Listen for property changes.
- Work with nested properties.
To create an observable object, use new can.Observe( [data] ) like:
var paginate = new can.Observe( { offset: 0, limit: 100, count: 2000 } )
To create an observable array, use new can.Observe.List( [array] ) like:
var hobbies = new can.Observe.List( ['programming',
'basketball',
'party rocking'] )
can.Observe is used by both can.Model and can.route. However, observe is useful on its own to maintain client-side state (such as pagination data).
attr observe.attr( [name,] [value] )
can.Observe.prototype.attr reads or sets properties on an observe:
paginate.attr( 'offset' ) //-> 0
paginate.attr( 'offset', 100 );
paginate.attr() //-> { offset: 100, limit: 100, count: 2000 }
paginate.attr( { limit: 200, count: 1000 } );
removeAttr observe.removeAttr( name )
can.Observe.prototype.removeAttr removes a property by name from an observe. This is similar to using the delete keyword to remove a property.
o = new can.Observe( { foo: 'bar' } );
o.removeAttr( 'foo' ); //-> 'bar'
bind observe.bind( eventType, handler(args...) )
can.Observe.prototype.bind listens to changes on a can.Observe. There are two types of events triggered as a result of an attribute change:
changeevents - a generic event so you can listen to any property change and how it was changedATTR_NAMEevents - bind to specific attribute changes
The following listens to all attribute changes and ‘offset’ changes on the paginate instance:
paginate.bind( 'change', function( ev, attr, how, newVal, oldVal ) {
// attr = 'offset'
// how = 'set'
// newVal = 200
// oldVal = 100
}).bind( 'offset', function( ev, newVal, oldVal ) {
// newVal = 200
// oldVal = 100
})
paginate.attr( 'offset', 200 );
unbind observe.unbind( eventType, handler )
can.Observe.prototype.unbind stops listening to an event. The same function that was used for the handler in bind must be passed to unbind.
var countHandler = function( ev, newVal, oldVal ) {
console.log( 'the count has changed' );
}
paginate.bind( 'count', countHandler );
paginate.attr( 'count', 3000 );
paginate.unbind( 'count', countHandler );
each observe.each( handler(value, attrName) )
can.Observe.prototype.each iterates through each attribute, calling handler with each attribute value and name.
paginate.each(function( value, name ) {
console.log( name, value );
})
// writes:
// offset 200
// limit 200
// count 1000
can.Observe.List new can.Observe.List( [array] )
can.Observe.List inherits from can.Observe but adds list specific methods such as:
- indexOf
list.indexOf( item )- Returns the position of the item in the list. - pop
list.pop()- removes the last item in the list. - push
list.push( items... )- adds items to the end of the list. - shift
list.shift()- removes the first item in the list. - splice
list.splice( index, howMany, [ items... ] )- removes and inserts items at the specified index. - unshift
list.unshift( items... )- adds items to the start of the list.
var hobbies = new can.Observe.List( [ 'programming',
'basketball',
'party rocking' ] )
// listen to changes in the list
hobbies.bind( 'add', function( ev, newVals, index ) {
console.log( 'added', newVals, 'at', index );
}).bind( 'remove', function( ev, oldVals, index ) {
console.log( 'removed', oldVals, 'at', index );
})
// modify the list
hobbies.pop()
hobbies.unshift( 'rocking parties' )
can.compute can.compute( [getterSetter,] [context] ) -> compute
can.compute represents some value that can be:
- read - by calling the compute like
compute() - updated - by passing a new value like
compute("new value") - listened to for changes - like
compute.bind("change",handle(ev,newVal,oldVal))
The value the compute represents can be:
- A static JavaScript value like
"Hello"or{foo: "bar"} - A composite value of one or more can.Observe property values.
- A converted value derived from another value.
Static Values
can.compute([value]) creates a computed with some value. For example
// create a compute
var age = can.compute(29);
// read the value
console.log("my age is currently", age());
// listen to changes in age
age.bind("change", function(ev, newVal, oldVal){
console.log("my age changed from",oldVal,"to",newVal)
})
// update the age
age(30);
Composite values
can.compute( getter(), context ) creates a compute that represents a composite value of one or more can.Observe properties and can.computes. The following fullName compute represents the person observe’s first and last name:
var person = new can.Observe({
first : "Justin",
last : "Meyer"
});
var fullName = can.compute(function(){
return person.attr("first") +" "+ person.attr("last")
})
fullName() //-> "Justin Meyer"
fullName.bind("change", function(ev, newVal, oldVal){
console.log("fullName changed from", oldVal,"to",newVal)
});
person.attr({
first: "David",
last: "Luecke"
})
can.compute caches computed values so reads are fast.
Converted Values
can.compute( getterSetter( [newVal] ) ) can be used to convert one value into another. The following creates a percentage compute that ranges from 0-100 that is cross bound to an observe’s progress property that ranges from 0-1.
var project = new can.Observe({
progress : 0.5
});
var percentage = can.compute(function(newVal){
// are we setting?
if(newVal !=== undefined){
project.attr("progress", newVal / 100)
} else {
return project.attr("progress") * 100;
}
})
// We can read from percentage.
percentage() //-> 50
// Write to percentage,
percentage(75)
// but it updates project!
project.attr('progress') //-> 0.75
Batch Operations
Use can.Observe.startBatch and can.Observe.stopBatch to enable atomic/batch operations. The following prevents all events from being triggered on person and items until after can.Observe.stopBatch() is called.
var person = new can.Observe({first: "Josh", last: "Dean"}),
list = new can.Observe.List([
{selected: false},
{selected: true },
{selected: false}
]);
person.bind("change", function(){} );
list.bind("change", function(){} );
can.Observe.startBatch();
person.attr("first", "Joshua");
list.each( function( item ) {
item.attr('selected', true)
})
can.Observe.stopBatch();
Batching operations can improve performance, especially with live-binding.
can.Model can.Model( [classProperties,] [prototypeProperties] )
can.Model is a can.Observe that connects to a RESTful interface.
Extend can.Model with your domain specific methods and can.Model provides a set of methods for managing changes.
To create a Model class, call can.Model with:
- classProperties, including findAll, findOne, create, update, destroy properties, and
- any prototypeProperties helper methods.
Make a Todo model in todos.js like the following:
var Todo = can.Model({
findAll : 'GET /todos',
findOne : 'GET /todos/{id}',
create : 'POST /todos',
update : 'PUT /todos/{id}',
destroy : 'DELETE /todos/{id}'
}, {})
init new can.Model(attributes)
Create a todo instance like:
var todo = new Todo( { name: 'do the dishes' } );
attr model.attr( name, [value] )
can.Model.prototype.attr reads or sets properties on model instances. It works the same way as can.Observe.prototype.attr.
todo.attr( 'name' ) //-> 'do the dishes'
todo.attr( 'name', 'wash the dishes' );
todo.attr() //-> { name: 'wash the dishes' }
todo.attr( { name: 'did the dishes' } );
Talking to the server
Model uses static findAll, findOne, create, update, and destroy methods to create, read, update and delete (CRUD) model data on the server.
By filling these functions out, you are able to call findAll and findOne on the model to retrieve model instances and save and destroy on instances.
findAll findAll( params, success( models ), error() ) -> Deferred
can.Model.findAll retrieves multiple instances from the server:
Todo.findAll( {}, function( todos ) {
console.log( todos[0].name );
}) //-> Deferred
This makes a request to GET /todos which should return JSON like:
{
"data": [
{ "id" : 1, "name" : "do the dishes" },
{ "id" : 2, "name" : "mow the lawn" },
{ "id" : 3, "name" : "iron my shirts" }
]
}
Note: .findAll can also accept an array, but you probably should not be doing that.
The todos parameter is a can.Model.List of todo instances. Todo.findAll returns a deferred that resolves to the todos list.
findOne findOne( params, success( model ), error() ) -> Deferred
findOne retrieves a single model instance:
Todo.findOne( { id: 1 }, function( todo ) {
console.log( todo.name );
})
This makes a request to GET /todos/{id} which should return JSON like:
{
"id" : 1,
"name" : "do the dishes"
}
The todo parameter is model instance. Todo.findOne returns a deferred that resolves to the todo instance.
save todo.save( success( todo ), error() ) -> Deferred
can.Model.prototype.save creates or updates instances depending if the instance has already been created or not.
To create a todo on the server, create a todo instance and call save like the following:
var todo = new Todo({name: "mow lawn"})
todo.save(function( todo ) {
console.log( todo );
})
This makes a request to PUT /todos with name=mow lawn and should get a response with the id like:
{ "id" : 5 }
save calls back with the original todo instance and returns a deferred that resolves with the todo after it has been created on the server.
To update a todo on the server, change the attributes and call save again like the following:
var todo = new Todo( { name: 'mow lawn' } );
todo.save(function( todo ) {
console.log( 'created', todo );
todo.attr( 'name', 'mow my lawn' )
todo.save(function( todo ) {
console.log( 'updated', todo );
})
})
This makes a request to POST /todos/5 with name=mow my lawn and only needs to get a successful response.
destroy todo.destroy( success( todo ), error() ) -> Deferred
can.Model.prototype.destroy deletes a record on the server. You can do this like:
var todo = new Todo( { name: 'mow lawn' } );
todo.save(function( todo ) {
console.log( 'created', todo );
todo.destroy(function( todo ) {
console.log( 'destroyed', todo );
})
})
This makes a request to DELETE /todos/5 and only needs a successful response. Like save, the callback’s todo parameter is the destroyed instance and a deferred is returned that resolves with the todo after it has been destroyed by the server.
bind model.bind( event, handler( ev, model ) ) -> model
can.Model.prototype.bind listens to changes in a model instance’s attributes in the same way as Observe’s bind. For example:
todo.bind( 'name', function( ev, newVal, oldVal ) {
console.log( 'name changed to', newVal );
})
In addition to Observe’s events, Model also supports three new events:
- created - an instance is created on the server
- updated - an instance is updated on the server
- destroyed - an instance is destroyed on the server
For example, listen for when an instance is created on the server like:
var todo = new Todo( { name: 'mow lawn' } );
todo.bind( 'created', function( ev, todo ) {
console.log( 'created', todo );
})
todo.save()
can.Model.bind lets you listen for anytime any instance is created, updated, or destroyed:
Todo.bind( 'created', function( ev, todo ){
console.log( 'created', todo );
})
can.Model.List new can.Model.List( items )
can.Model.List is a can.Observe.List that automatically removes items when they are destroyed. Model.Lists are returned by findAll.
Todo.findAll( {}, function( todos ) {
// listen for when a todo is removed
todos.bind( 'remove', function( ev, removed, index ) {
console.log( 'removed', removed.length, 'todos' );
})
// destroy the first todo
todos[0].destroy()
})
can.view can.view( idOrUrl, data ) -> documentFragment
can.view is used to load, render, and create HTMLElements from JavaScript templates. Pass it …
- the id or URL of a script tag to use as the content of the template
- data to pass to the template
It returns the rendered result of the template as a documentFragment (a documentFragment is a lightweight container that can hold DOM elements in it).
document.getElementById( 'todos' )
.appendChild( can.view( 'todos.ejs', [ { name: 'mow lawn' } ] ) )
can.view supports multiple templating languages; however, can.EJS is packaged with CanJS and supports live-binding of can.Observe.
Loading Templates
can.view loads templates from a URL or a script tag. To load from a script tag, create a script tag with the template contents, an id, and a type attribute that specifies the template type (text/ejs).
For example, add the following html:
<script type="text/ejs" id="todosEJS">
<% for( var i = 0; i < this.length; i++ ) { %>
<li><%= this[ i ].name %></li>
<% } %>
</script>
Render this template and insert it into the page:
Todo.findAll( {}, function( todos ) {
document.getElementById( 'todos' )
.appendChild( can.view( 'todosEJS', todos ) );
} );
To load from a URL, create a todos/todos.ejs file that contains:
<% for( var i = 0; i < this.length; i++ ) { %>
<li><%= this[ i ].name %></li>
<% } %>
Render this with:
Todo.findAll( {}, function( todos ) {
document.getElementById( 'todos' )
.appendChild( can.view( 'todos/todos.ejs', todos ) );
} );
Deferreds
can.view accepts deferreds. If the data argument is a deferred or an object that contains deferreds, can.view returns a deferred that resolves to the documentFragment after all deferreds have resolved and the template has loaded.
can.Model.findAll returns deferreds. This means that the following loads todos/todos.ejs, Todo.findAll and User.findOne in parallel and resolves the returned deferred with the documentFragment when they are all done:
can.view( 'todos/todos.ejs', {
todos: Todo.findAll(),
user: User.findOne( { id: 5 } )
} ).then(function( frag ){
document.getElementById( 'todos' )
.appendChild( frag );
})
render can.view.render( idOrUrl, data )
To render a string instead of a documentFragment, use can.view.render like:
<% for( var i = 0; i < todos.length; i++ ) { %>
<li><%== can.view.render( '/todos/todo.ejs', {
todo: todo[ i ]
} ) %>
</li>
<% } %>
can.EJS new can.EJS( options )
can.EJS is CanJS’s default template language and used with can.view. It provides live binding when used with can.Observes. A can.EJS template looks like the HTML you want, but with magic tags where you want dynamic behavior. The following lists todo elements:
<script type="text/ejs" id="todosEJS">
<% for( var i = 0; i < this.length; i++ ) { %>
<li><%= this[ i ].name %></li>
<% } %>
</script>
Use can.view to render this template:
Todo.findAll( {}, function( todos ) {
document.getElementById( 'todos' )
.appendChild( can.view( 'todosEJS', todos ) )
}
Notice that this in the template is the list of todos. The data argument passed can.view becomes this in EJS. EJS can also access any properties of this directly (without writing this.PROPERTY all the time). For example, a template that lists the user’s name and todos:
<script type="text/ejs" id="todosEJS">
<h2><%= user.name %></h2>
<% for( var i = 0; i < todos.length; i++ ) { %>
<li><%= todos[ i ].name %></li>
<% } %>
</script>
Can be inserted into the document with:
can.view( 'todosEJS', {
todos : Todo.findAll(),
user: User.findOne( { id: 5 } )
}).then(function( frag ) {
document.getElementById( 'todos' )
.appendChild( frag );
})
Magic Tags
EJS uses 5 types of magic tags:
<% CODE %> - Runs JS Code.
This type of magic tag does not modify the template but is used for JS control statements like for-loops, if/else, switch, declaring variables, etc. Pretty much any JS code is valid. Examples:
<!-- check if there are no todos -->
<% if ( todos.attr( 'length' ) === 0 ) { %>
<li>You have no todos</li>
<% } else { %>
<% todos.each(function( todo ) { %>
<li> .... </li>
<% } ) %>
<% } %>
<!-- create and use a variable -->
<% var person = todo.attr( 'person' ) %>
<span><%= person.attr( 'name' ) %><span>
<%= CODE %> - Runs a JS statement and writes the escaped result into the result of the template.
The following results in the user seeing my favorite element is <b>B<b>. and not my favorite element is B..
<div>my favorite element is <%= '<b>B</b>' %>.</div>
This protects you against cross-site scripting attacks.
<%== CODE %> - Runs a JS statement and writes the unescaped result into the result of the template.
The following results in my favorite element is B.. Using <%== is useful for sub-templates.
<div>my favorite element is <%== '<B>B</B>' %>.</div>
Use <%== CODE %> when rendering subtemplates:
<% for( var i = 0; i < todos.length; i++ ) { %>
<li><%== can.view.render( 'todoEJS', todos[ i ] ) %></li>
<% } %>
Live Binding
can.EJS will automatically update itself when can.Observes change. To enable live-binding, use attr to read properties. For example, the following template will update todo’s name when it change:
<li><%= todo.attr( 'name' ) %></li>
Notice attr( 'name' ). This sets up live-binding. If you change the todo’s name, the <li> will automatically be updated:
todo.attr( 'name', 'Clean the toilet' );
Live-binding works by wrapping the code inside the magic tags with a function to call when the attribute (or attributes) are changed. This is important to understand because a template like this will not work:
<% for( var i = 0; i < todos.length; i++ ) { %>
<li><%= todos[ i ].attr( 'name' ) %></li>
<% } %>
This does not work because when the function wrapping todos[ i ].attr( 'name' ) is called, i will be 3, which is the index of the last todo, not the index of the desired todo. Fix this by using a closure like:
<% $.each( todos, function( i, todo ) { %>
<li><%= todo.attr( 'name' ) %></li>
<% } ) %>
each observe.each( iterator( item, index ) )
If you want to make the previous template update when todos are added or removed, you could read length like:
<% todos.attr( 'length');
$.each( todos, function( i, todo ) { %>
<li><%= todo.attr( 'name' ) %></li>
<% }) %>
Or simply use can.Observe’s each method like:
<% todos.each( function( todo ) { %>
<li><%= todo.attr( 'name' ) %></li>
<% }) %>
Now when todos are added or removed from the todo list, the template’s HTML is updated:
// add an item
todos.push( new Todo( { name: 'file taxes' } ) );
// destroying an item removes it from Model.Lists
todos[ 0 ].destroy()
Element Callbacks
If a function is returned by the <%= %> or <%== %> magic tags within an element’s tag like:
<div <%= function( element ) { element.style.display = 'none' } %> >
Hello
</div>
The function is called back with the HTMLElement as the first argument. This is useful to initialize functionality on an element within the view. This is so common that EJS supports ES5 arrow functions that get passed the NodeList wrapped element. Using jQuery, this lets you write the above callback as:
<div <%= (el) -> el.hide() %> >
Hello
</div>
This technique is commonly used to add data, especially model instances, to an element like:
<% todos.each( function( todo ) { %>
<li <%= (el) -> el.data( 'todo', todo ) %>>
<%= todo.attr( 'name' ) %>
</li>
<% } ) %>
jQuery’s el.data( NAME, data ) adds data to an element. If your library does not support this, can provides it as can.data( NodeList, NAME, data ). Rewrite the above example as:
<% list(todos, function( todo ) { %>
<li <%= (el) -> can.data( el, 'todo', todo ) %>>
<%= todo.attr( 'name' ) %>
</li>
<% } ) %>
can.Mustache new can.Mustache( options )
can.Mustache provides logic-less templates with live binding when used with can.Observes. It currently ships as a plugin:
Download can.Mustache (Annotated source)
Mustache and Handlebar templates are compatible with can.Mustache, so you can import existing templates.
Getting Started
Mustache templates look similar to normal HTML except they contain keys for inserting data into the template and sections to enumerate and/or filter the enclosed template blocks.
The following lists todo elements:
Mustache Template
<script id="template" type="text/mustache">
<ul>
{{#todos}}
<li>{{.}}</li>
{{/todos}}
</ul>
</script>
The Mustache syntax includes the {{ }} magic tags above.
JavaScript
var list = new can.Observe.List([ 'Take out the trash' ]);
var template = can.view("#template", {todos: list});
can.$(document.body).append(template);
will render:
<ul>
<li>Take out the trash</li>
</ul>
Now to update the list with a new todo:
list.push('Get groceries');
which will re-render to:
<ul>
<li>Take out the trash</li>
<li>Get groceries</li>
</ul>
Magic Tags
Mustache has 3 different types of magic tags:
{{ }}Mustache will escape values enclosed in these tags.{{{ }}}Mustache will un-escape values enclosed in these tags.{{! }}Mustache will ignore values enclosed in these tags.
Helpers
Helpers allow you to register functions that can be called from any context in a template. Since Mustache templates are “logic-less”, all your logic will be contained in helper functions.
Adding Helpers
To register a helper local to the template you’re rendering, pass a helpers object as the third param of can.view:
var frag = can.view("#template", {todos: list}, {
canCheck: function(val){
if(val){
return options.fn(this)
}
}
});
Note that if a can.Observe attribute is passed as an argument to a helper, it is converted to a can.compute getter/setter function. For example in your template:
<div>{{addPrefix name}}</div>
Your helper would look like:
var item = new can.Observe({name: "Brian"}),
frag = can.view("#template", item, {
addPrefix: function(name){
return "Mr." + name()
}
});
To register a global helper, use the can.Mustache.registerHelper method.
can.Mustache.registerHelper('l10n', function(str, options){
return (Globalize != undefined ? Globalize.localize(str) : str);
});
Any Mustache template can access it like:
<div>{{l10n 'JavaScript'}}</div>
Element Callbacks
When rendering the view, it’s common to want to call some JavaScript on a specific element such as intializing a jQuery plugin on the new HTML. Mustache makes this easy to define this code in the mark-up using ES5 Arrow Syntax. For example:
<div class="tabs" {{(el) -> el.jquery_tabs()}}></div>
Data Helpers
Associating data to an element is easy in Mustache. Call the data helper followed by the attribute name you want to attach it as. For example:
{
name: 'Austin'
}
<ul>
<li id="personli" {{data 'person'}}>{{name}}</li>
</ul>
Now I can access my object by doing:
var nameObject = can.data(can.$('#personli'), 'name');
It automatically attaches the data to the element using can.data with implied context of this.
Keys and Sections
Mustache HTML contains keys for inserting data into the template and sections to enumerate and/or filter the enclosed template blocks.
Keys
Keys insert data into the template. They reference variables within the current context. For example:
{
name: "Austin"
}
{{name}}
would render:
"Austin"
Sections
Sections contain text blocks and evaluate whether to render it or not. If the object evaluates to an array it will iterate over it and render the block for each item in the array. There are four different types of sections.
Falseys and Empty Arrays
If the value returns a false, undefined, null, "" or [] we consider that a falsey value.
If the value is falsey, the section will NOT render the block between the pound and slash.
{
friends: false
}
{{#friends}}
Never shown!
{{/friends}}
Arrays
If the value is a non-empty array, sections will iterate over the array of items, rendering the items in the block.
For example, a list of friends will iterate over each of those items within a section.
{
friends: [
{ name: "Austin" },
{ name: "Justin" }
]
}
<ul>
{{#friends}}
<li>{{name}}</li>
{{/friends}}
</ul>
would render:
<ul>
<li>Austin</li>
<li>Justin</li>
</ul>
Truthy
Truthy sections match when the value is a non-falsey object but not a list and render the block accordingly.
{
friends: { name: "Jon" }
}
<ul>
{{#friends}}
<li>{{name}}</li>
{{/friends}}
</ul>
would render:
<ul>
<li>Jon</li>
</ul>
Inverted
Inverted sections match falsey values. An inverted section syntax is similar to regular sections except it begins with a caret rather than a pound. If the value referenced is falsey, the section will render.
{
friends: []
}
<ul>
{{#friends}}
</li>{{name}}</li>
{{/friends}}
{{^friends}}
<li>No friends.</li>
{{/friends}}
</ul>
would render:
<ul>
<li>No friends.</li>
</ul>
Paths and Context
Paths allow you to reference variables relative to the current context.
When Mustache is resolving a object in a section, it sets the current context to the value for which its iterating. For example:
{
friends: [ 'Austin' ]
}
{{#friends}}
{{.}}
{{/friends}}
The . would represent the ‘Austin’ value in the array.
Internally, Mustache keeps a stack of contexts as the template dives deeper into nested sections and helpers. If a key is not found within the current context, Mustache will look for the key in the parent context and so on until it resolves the object or reaches the parent most object. For example:
{
family: [
{
name: 'Austin',
sisters: [
{
name: 'Katherine'
}
],
brothers: [
{
name: 'Justin'
}
]
}
]
}
{{#family}
{{#brothers}}
{{#sisters}}
{{name}}
{{/sisters}}
{{/brothers}}
{{/family}}
Since sisters isn’t in the context of the brothers array, it jumps up to the family object and resolves sisters there.
Partials
Partials are templates embedded in other templates which execute at runtime. Partials begin with a greater than sign, like {{>my_partial}}.
Partials are rendered at runtime, so recursive partials are possible but make sure you avoid infinite loops. They also inherit the calling context.
For example, this template and partial:
base.mustache
<h2>Names</h2>
{{#names}}
{{>user.mustache}}
{{/names}}
user.mustache
<strong>{{name}}</strong>
The resulting expanded template at render time would look like:
<h2>Names</h2>
{{#names}}
<strong>{{name}}</strong>
{{/names}}
can.Control can.Control(classProps, prototypeProps)
can.Control creates organized, memory-leak free, rapidly performing, stateful controls. Use it to create UI controls like tabs, grids, and context menus and organizes them into higher-order business rules with can.route. It can serve as a traditional view and a traditional controller.
The following examples make a basic todos widget that lists todos and lets us destroy them. Create a control constructor function of your own by extending can.Control.
var Todos = can.Control({
'init': function( element , options ) {
var self = this;
Todo.findAll( {}, function( todos ) {
self.element.html( can.view( 'todosEJS', todos ) )
})
}
})
Create an instance of the Todos on the #todos element with:
var todosControl = new Todos( '#todos', {} );
todos.ejs looks like:
<% todos.each( function( todo ) { %>
<li <%= (el) -> el.data( 'todo', todo ) %> >
<%= todo.attr( 'name' ) %>
<a href="javascript://" class="destroy">X</a>
</li>
<% } ) %>
init can.Control.prototype.init(element, options)
init is called when a new can.Control instance is created. It’s called with:
- element - The wrapped element passed to the control. Control accepts a raw HTMLElement, a CSS selector, or a NodeList. This is set as this.element on the control instance.
- options - The second argument passed to new Control, extended with the can.Control’s static defaults. This is set as this.options on the control instance.
and any other arguments passed to new can.Control(). For example:
var Todos = can.Control({
defaults: { view: 'todos.ejs' }
}, {
'init': function( element , options ){
var self = this;
Todo.findAll( {},function( todos ) {
self.element.html( can.view( self.options.view, todos ) )
});
}
})
// create a Todos with default options
new Todos( document.body.firstElementChild );
// overwrite the template option
new Todos( $('#todos'), { view: 'specialTodos.ejs' } )
element this.element
this.element is the NodeList of a single element, the element the control is created on.
var todosControl = new Todos( document.body.firstElementChild );
todosControl.element[0] //-> document.body.firstElementChild
Each library wraps the element differently. If you are using jQuery, the element is wrapped with jQuery( element ).
options this.options
this.options is the second argument passed to new can.Control() and is merged with the control’s static defaults property.
Listening to events
Control automatically binds prototype methods that look like event handlers. Listening to clicks on <li> elements looks like:
var Todos = can.Control({
'init' : function( element , options ) {
var self = this;
Todo.findAll( {}, function( todos ) {
self.element.html( can.view('todosEJS', todos ) )
});
},
'li click' : function( li, event ) {
console.log( 'You clicked', li.text() )
// let other controls know what happened
li.trigger( 'selected' );
}
})
When an <li> is clicked, 'li click' is called with:
- The library-wrapped element that was clicked.
- The event data
Control uses event delegation, so you can add <li>s without needing to rebind event handlers.
To destroy a todo when it’s <a href="javascript://" class="destroy"> link is clicked:
var Todos = can.Control({
'init' : function( element , options ) {
var self = this;
Todo.findAll( {},function( todos ) {
self.element.html( can.view( 'todosEJS', todos ) )
});
},
'li click': function( li ) {
li.trigger( 'selected', li.data( 'todo' ) );
},
'li .destroy click': function( el, ev ) {
// get the li element that has todo data
var li = el.closest( 'li' );
// get the model
var todo = li.data( 'todo' )
// destroy it
todo.destroy();
}
})
When the todo is destroyed, EJS’s live binding will remove its LI automatically.
Templated Event Handlers Pt 1 "{optionName}"
Customize event handler behavior with "{NAME}" in the event handler name. The following allows customization of the event that destroys a todo:
var Todos = can.Control( 'Todos', {
'init': function( element , options ) { ... },
'li click': function( li ) { ... },
'li .destroy {destroyEvent}': function( el, ev ) {
// previous destroy code here
}
})
// create Todos with this.options.destroyEvent
new Todos( '#todos', { destroyEvent: 'mouseenter' } )
Values inside {NAME} are looked up on the control’s this.options and then, if not found, on the window. For example, we could customize it instead like:
var Todos = can.Control( 'Todos', {
'init': function( element , options ) { ... },
'li click': function( li ) { ... },
'li .destroy {Events.destroy}': function( el, ev ) {
// previous destroy code here
}
})
// Events config
Events = { destroy: 'click' };
// Events.destroy is looked up on the window.
new Todos( '#todos' )
The selector can also be templated.
Templated Event Handlers Pt 2 "{objectName}"
Control can also bind to objects other than this.element with templated event handlers. This is critical for avoiding memory leaks that are common among MVC applications.
If the value inside {NAME} is an object, the event will be bound to that object rather than the control. For example, the following tooltip listens to clicks on the window:
var Tooltip = can.Control({
'{window} click': function( el, ev ) {
// hide only if we clicked outside the tooltip
if (!this.element.has( ev.target ).length ) {
this.element.remove();
}
}
})
// create a Tooltip
new Tooltip( $('<div>INFO</div>').appendTo(el) )
This is convenient when needing to listen to model changes. If EJS was not taking care of removing <li>s after their model was destroyed, we could implement it in Todos like:
var Todos = can.Control({
'init': function( element , options ) {
var self = this;
Todo.findAll( {}, function( todos ) {
self.todosList = todos;
self.element.html( can.view( 'todosEJS', todos ) )
})
},
'li click': function( li ) {
li.trigger( 'selected', li.data( 'todo' ) );
},
'li .destroy click': function( el, ev ) {
// get the li element that has todo data
var li = el.closest( 'li' );
// get the model
var todo = li.data( 'todo' )
//destroy it
todo.destroy();
},
'{Todo} destroyed': function( Todo, ev, todoDestroyed ) {
// find where the element is in the list
var index = this.todosList.indexOf( todoDestroyed )
this.element.children( ':nth-child(' + ( index + 1 ) + ')' )
.remove()
}
})
new Todos( '#todos' );
destroy control.destroy()
can.Control.prototype.destroy unbinds a control’s event handlers and releases its element, but does not remove the element from the page.
var todosControl = new Todos( '#todos' )
todosControl.destroy();
When a control’s element is removed from the page, destroy is called automatically.
new Todos( '#todos' )
$( '#todos' ).remove();
All event handlers bound with Control are unbound when the control is destroyed (or its element is removed).
A brief aside on destroy and templated event binding: Taken together, templated event binding and Control’s automatic clean-up make it almost impossible to write leaking applications. An application that uses only templated event handlers on controls within the body could free up all data by calling $( document.body ).empty().
on control.on()
can.Control.prototype.on rebinds a control’s event handlers. This is useful when you are listening to a specific model instance, and want to change to change to another.
The following Editor widget’s todo method updates the control’s todo option and then calls on() to rebind '{todo} updated'.
var Editor = can.Control({
todo: function( todo ) {
this.options.todo = todo;
this.on();
this.setName();
},
// a helper that sets the value of the input
// to the todo's name
setName: function() {
this.element.val( this.options.todo.name );
},
// listen for changes in the todo
// and update the input
'{todo} updated': function() {
this.setName();
},
// when the input changes
// update the todo instance
'change': function() {
var todo = this.options.todo
todo.attr( 'name', this.element.val() )
todo.save();
}
});
var todo1 = new Todo( { id: 6, name: 'trash' } ),
todo2 = new Todo( { id: 6, name: 'dishes' } );
// create the editor
var editor = new Editor( '#editor' );
// show the first todo
editor.todo( todo1 )
// switch it to the second todo
editor.todo( todo2 );
can.route can.route( route, [defaults] )
can.route is the core of CanJS’s routing functionality. It is a special can.Observe that updates window.location.hash when its properties change and updates its properties when window.location.hash changes. can.route uses routes to translate URLs into property values. If no routes are provided, it just serializes the route into standard URL-encoded notation. For example:
// empty the hash
window.location.hash = ''
// the route is empty
can.route.attr() //-> {}
// set the hash
window.location.hash = '#!id=7'
// the route data reflects what's in the hash
can.route.attr() //-> { id: 7 }
// set the route data
can.route.attr( { type : 'todos' } )
// the hash changes to reflect the route data
window.location.hash //-> #!type=todos
// set a property on the hash
can.route.attr( 'id', 5 )
// the hash changes again to reflect the route data
window.location.hash //-> #!type=todos&id=5
Use can.route( route, defaults ) to make pretty URLs:
// create a route
can.route( ':type/:id' )
// set the hash to look like the route
window.location.hash = '#!todo/5'
// the route data changes accordingly
can.route.attr() //-> { type: 'todo', id: 5 }
// change the route data with properties
// used by the route
can.route.attr( { type: 'user', id: 7 } )
// the hash is changed to reflect the route
window.location.hash //-> '#!user/7'
// create a default route
can.route( '', { type: 'recipe' } )
// empty the hash
window.location.hash = '';
// the route data reflects the default value
can.route.attr() //-> { type: 'recipe' }
bind route.bind( eventName, handler( event ) )
Use bind to listen to changes in the route like:
can.route.bind( 'id', function( ev, newVal ) {
console.log( 'id changed' );
});
route events
Listen to routes in controls with special “route” events like:
var Routing = can.Control({
'route': function() {
// matches empty hash, #, or #!
},
'todos/:id route': function( data ) {
// matches routes like #!todos/5
}
})
// create routing control
new Routing( document.body );
The route methods get called back with the route data. The empty "route" will be called with no data. But, "todos/:id route" will be called with data like: {id: 6}.
We can update the route by changing can.route’s data like:
can.route.attr( 'id', '6' ) // window.location.hash = #!todos/6
url can.route.url( options, [merge] )
can.route.url takes attributes and creates a URL that can be used in a link.
var hash = can.route.url({id: 7}) // #!todos/7
window.location.hash = hash;
link can.route.link( name, options, props, merge )
can.route.link is used to create a link.
var link = can.route.link( 'Todo 5',
{ id: 5 },
{ className : 'button' } );
link //-> <a href="#!todos/7" class="button">Todo 5</a>
The following enhances the Routing control to listen for ".todo selected" events and change can.route. When the can.route changes, it retrieves the todo from the server and updates the editor widget.
var Routing = can.Control({
init: function() {
this.editor = new Editor( '#editor' )
new Todos( '#todos' );
},
// the index page
'route': function() {
$('#editor').hide();
},
'todos/:id route': function( data ) {
$("#editor").show();
var self = this;
Todo.findOne( data, function( todo ) {
this.editor.update( { todo: todo } );
})
},
'.todo selected': function( el, ev, todo ) {
can.route.attr( 'id', todo.id );
}
});
// create routing control
new Routing( document.body );
The Routing control is a traditional controller. It coordinates the Editor and Todos controls with can.route hash data. Editor and Todos are traditional views, consuming models.
If you can understand this, you’re on your way to mastery of the CanJS MVC architecture. Congrats! See it in action.
Utilities
CanJS provides several utility methods. Usually, they are mapped to the underlying library. But, by using only these methods, you can create plugins that work with any library. Also, these methods are required to run CanJS from another library.
String Helpers
// remove leading and trailing whitespace
can.trim( ' foo ' ) // -> 'foo'
// escape HTML code
can.esc( '<foo>&<bar>' ) //-> '<foo<&<bar<'
// looks up an object by name
can.getObject( 'foo.bar', [ { foo: { bar: 'zed' } } ] ) //-> 'zed'
// capitalize a string
can.capitalize( 'fooBar' ) //-> 'FooBar'
// micro templating
can.sub( '{greet} world', { greet: 'hello' } ) //-> 'hello world'
// deparams a form encoded URL into an object
can.deparam( 'foo=bar&hello=world' )
//-> { foo: 'bar', hello: 'world' }
Array Helpers
// convert array-like data into arrays
can.makeArray( { 0: "zero", 1: 'one', length: 2 } ) // -> [ 'zero', 'one' ]
// return if an array is an array
can.isArray( [] ) //-> true
// converts one array to another array
can.map( [ { prop: 'val1' }, { prop: 'val2' } ], function( val, prop ) {
return val
}) //-> [ 'val1', 'val2' ]
// iterates through an array
can.each( [ { prop: 'val1' }, { prop: 'val2' } ], function( value, index ) {
// function called with
// value = { prop: 'val1' }, index = 0
// value = { prop: 'val2' }, index = 1
})
Object Helpers
// extends one object with the properties of another
var first = {},
second = { a: 'b' },
third = { c: 'd' };
can.extend( first, second, third ); //-> first
first //-> { a: 'b', c: 'd' }
second //-> { a: 'b' }
third //-> { c: 'd' }
// deep extends one object with another
can.extend( true, first, second, third );
// parameterize into a query string
can.param( { a: 'b', c: 'd' } ) //-> 'a=b&c=d'
// returns if an object is empty
can.isEmptyObject( {} ) //-> true
can.isEmptyObject( { a: 'b' } ) //-> false
Function Helpers
// returns a function that calls another function
// with 'this' set.
var func = can.proxy(function( one ) {
return this.a + one
}, { a: 'b' } );
func( 'two' ) //-> 'btwo'
// returns if an object is a function
can.isFunction( {} ) //-> false
can.isFunction( function() {} ) //-> true
Event Helpers
// binds handler on obj's eventName event
can.bind( obj, eventName, handler )
// unbind handler on obj's eventName event
can.unbind( obj, eventName, handler )
// binds handler on all elements' eventName event that match selector
can.delegate( obj, selector, eventName, handler )
// unbinds handler on all elements' eventName event that match selector in obj
can.undelegate( obj, selector, eventName )
// executes all handlers attached to obj for eventName
can.trigger( obj, eventName, args )
Deferred
can.Deferreds are explained in greater detail in the DoneJS documentation.
// creates a new Deferred object
var deferred = new can.Deferred()
// pipes a deferred into another deferred
deferred.pipe(function() {
// done handler
}, function() {
// fail handler
})
// resolves a deferred (deferreds piped into the deferred also become resolved)
deferred.resolve()
// rejects a deferred (deferreds piped into the deferred also become rejected)
deferred.reject()
// used to execute callback functions when all passed deferreds become resolved
can.when()
Ajax
// performs an asynchronous HTTP request
can.ajax({
url : 'url',
type: 'GET', // 'POST'
async : false,
dataType: 'json',
success: function() {},
error: function() {}
}) //-> deferred
HTMLElement Helpers
can.buildFragment( frags, nodes )
// a node list
can.$( 'div.bar' ) //-> []
// appends html to the HTMLElements in the NodeList
can.append( NodeList, html )
// removes the HTMLElements in the NodeList from the DOM
can.remove( NodeList )
// stores arbitrary data to the HTMLElements in the NodeList
can.data( NodeList, dataName, dataValue )
// adds CSS class(es) to the HTMLElements in the NodeList
can.addClass( NodeList, className )
HTMLElement ‘destroyed’ event
When an element is removed from the page using any library, CanJS triggers a ‘destroyed’ event on the element. This is used to teardown event handlers in can.Control.
Using CanJS
CanJS can be used with jQuery, Dojo, Mootools, YUI and Zepto and as AMD modules with any of these libraries.
AMD
The CanJS Download contains an amd folder which allows you to load any CanJS component and plugin using an AMD module loader like RequireJS. jQuery will be the default library so make sure the jquery module id points to the jQuery source. Here is an example for jQuery and RequireJS:
<script type="text/javascript" src="require.js"></script>
<script type="text/javascript">
require.config({
paths : {
"jquery" : "http://code.jquery.com/jquery-1.8.2"
}
});
require(['can/view/ejs', 'can/control'], function(can) {
// Use EJS and Control
});
</script>
The can module is a shortcut that loads CanJS’s core plugins (Construct, Control, route, Model, view, and EJS) and returns the can namespace.
require(['can'], function(can) {
// Use can.Control, can.view, can.Model etc.
});
If you would like to use another library, map the can/util/library module to can/util/dojo, can/util/zepto, can/util/yui or can/util/mootools.
With RequireJS and Zepto, it loks like this:
require.config({
map : {
'*' : {
"can/util/library" : "can/util/zepto"
}
},
paths: {
"zepto" : "http://cdnjs.cloudflare.com/ajax/libs/zepto/1.0rc1/zepto.min"
}
});
jQuery
CanJS supports jQuery 1.8+. Include a copy of jQuery along with CanJS to get started.
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.js">
</script>
<script src="can.jquery.js"></script>
<script>
// start using CanJS
can.Model('Todo', {
...
});
</script>
CanJS supports binding to any jQuery objects (like jQuery UI widgets) that use standard jQuery events. The jQuery UI Datepicker doesn’t have built-in support for standard jQuery events, so for those cases, a workaround should be applied:
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.js">
</script>
<script src="jquery.ui.core.js"></script>
<script src="jquery.ui.datepicker.js"></script>
<script src="can.jquery.js"></script>
<script>
// create models
Todo = can.Model({ ... });
Todo.List = can.Model.List({ ... });
// create control
Todos = can.Control({
// listen to the calendar widget's datepickerselect event
'{calendar} datepickerselect': function(calendar, ev){
// do something with the selected date
var selectedDate = this.options.calendar.datepicker('getDate');
...
}
});
// Initialize the app
Todo.findAll({}, function(todos) {
new Todos('#todoapp', {
todos: todos,
calendar: $('#calendar').hide().datepicker({
// Adding a workaround for date selection since the
// jQuery UI datepicker widget doesn't fire the
// "datepickerselect" event
onSelect: function(dateText, datepicker) {
$(this).trigger({
type: 'datepickerselect',
text: dateText,
target: datepicker
});
}
})
});
});
</script>
Dojo
CanJS supports Dojo 1.8+ using its new AMD loader in asynchronous or synchronous mode. Everything described in the using CanJS and AMD section applies to Dojo as well. An example configuration that uses the AMD files from the CanJS CDN can look like this:
require({
aliases:[
['can/util/library', 'can/util/dojo']
],
baseUrl : 'http://canjs.us/release/latest/amd/can.js',
});
require(['can/control'], function(Control) {
// Use Control
});
Mootools
CanJS supports Mootools 1.4+. Include a copy of Mootools Core along with CanJS to get started.
Mootools Core has an issue where focus and blur events are not fired for delegate event listeners. Include Mootools More’s Event.Pseudos module for focus and blur support.
<script src="https://ajax.googleapis.com/ajax/libs/mootools/1.4.5/
mootools.js"></script>
<!-- Mootools More Event.Pseudos module -->
<script src="mootools-more-event_pseudos-1.4.0.1.js"></script>
<script src="can.mootools.js"></script>
<script>
// start using CanJS
Todo = can.Model({
...
});
</script>
YUI
CanJS supports YUI 3.4+ with both dynamically or statically loaded modules. CanJS depends on the following YUI modules: node, io-base, querystring, event-focus, and array-extras. The selector-css2 and selector-css3 YUI modules are optional, but necessary for IE7 and other browsers that don’t support querySelectorAll.
To use with dynamically loaded modules, include the YUI loader along with CanJS. Add 'can' to your normal list of modules with YUI().use('can', ...) wherever CanJS will be used.
<script src="http://yui.yahooapis.com/3.4.1/build/yui/yui-min.js"></script>
<script src="can.yui.js"></script>
<script>
// CanJS with support for modern browsers
YUI().use('can', function(Y) {
// start using CanJS
Todo = can.Model({
...
});
});
// CanJS with support for IE7 and other browsers without querySelectorAll
YUI({ loadOptional: true }).use('can', function(Y) {
// start using CanJS
Todo = can.Model({
...
});
});
</script>
To use with statically loaded modules, include a static copy of YUI (with the previously mentioned YUI dependencies) along with CanJS. CanJS will automatically be included wherever YUI().use('*') is used.
<!-- YUI Configurator: http://yuilibrary.com/yui/configurator/ -->
<script src="http://yui.yahooapis.com/combo?3.7.3/build/yui-base/yui-base-min.
js&3.7.3/build/oop/oop-min.js&3.7.3/build/event-custom-base/event-custom-base-
min.js&3.7.3/build/features/features-min.js&3.7.3/build/dom-core/dom-core-min.
js&3.7.3/build/dom-base/dom-base-min.js&3.7.3/build/selector-native/selector-n
ative-min.js&3.7.3/build/selector/selector-min.js&3.7.3/build/node-core/node-c
ore-min.js&3.7.3/build/node-base/node-base-min.js&3.7.3/build/event-base/event
-base-min.js&3.7.3/build/event-delegate/event-delegate-min.js&3.7.3/build/node
-event-delegate/node-event-delegate-min.js&3.7.3/build/pluginhost-base/pluginh
ost-base-min.js&3.7.3/build/pluginhost-config/pluginhost-config-min.js&3.7.3/b
uild/node-pluginhost/node-pluginhost-min.js&3.7.3/build/dom-style/dom-style-mi
n.js&3.7.3/build/dom-screen/dom-screen-min.js&3.7.3/build/node-screen/node-scr
een-min.js&3.7.3/build/node-style/node-style-min.js&3.7.3/build/querystring-st
ringify-simple/querystring-stringify-simple-min.js&3.7.3/build/io-base/io-base
-min.js&3.7.3/build/array-extras/array-extras-min.js&3.7.3/build/querystring-p
arse/querystring-parse-min.js&3.7.3/build/querystring-stringify/querystring-st
ringify-min.js&3.7.3/build/event-custom-complex/event-custom-complex-min.js&3.
4.1/build/event-synthetic/event-synthetic-min.js&3.7.3/build/event-focus/event
-focus-min.js"></script>
<script src="can.yui.js"></script>
<script>
// start using CanJS
Todo = can.Model({
...
});
</script>
CanJS can also bind to YUI widget events. The following example shows how to bind to the selectionChange event for a YUI Calendar widget:
YUI().use('can', 'calendar', function(Y) {
// create models
Todo = can.Model({ ... });
Todo.List = can.Model.List({ ... });
// create control
Todos = can.Control({
// listen to the calendar widget's selectionChange event
'{calendar} selectionChange': function(calendar, ev){
// do something with the selected date
var selectedDate = ev.newSelection[0];
...
}
});
// initialize the app
Todo.findAll({}, function(todos) {
new Todos('#todoapp', {
todos: todos,
calendar: new Y.Calendar({
contentBox: "#calendar"
}).render()
});
});
});
Zepto
CanJS supports Zepto 0.8+. Include a copy of Zepto along with CanJS to get started.
Zepto 0.8 has an issue where focus and blur events are not fired for delegate event listeners. There is a fix included for Zepto > 0.8, but you can apply this patch to zepto.js when using Zepto 0.8.
<!-- Zepto 0.8 with focus/blur patch applied -->
<script src="zepto.0.8-focusblur.js"></script>
<script src="can.zepto.js"></script>
<script>
// start using CanJS
Todo = can.Model({
...
});
</script>
Plugins
can.Construct.proxy construct.proxy( methodname, [ curriedArgs ] )
can.construct.proxy.js (Annotated source)
The can.Construct.proxy plugin adds a proxy method that takes a function name and returns a new function that will always have this set to the original context. You can also curry arguments that will be added to the beginning of the proxied functions argument list:
var Person = can.Construct({
init: function( name ) {
this.name = name;
},
sayName: function( text, end ) {
return text + this.name + end;
}
});
var instance = new Person( 'John' );
var callback = instance.proxy( 'sayName' );
var curriedCallback = instance.proxy( 'sayName', 'Hi my name is' );
callback( 'Hi I am ', ' :)' ); // -> 'Hi I am John :)'
curriedCallback( '!' ); // -> 'Hi my name is John!'
can.Construct.super
can.construct.super.js (Annotated source)
The can.Construct.super plugin provides access to overwritten methods using this._super when extending a can.Construct:
var ImprovedPerson = Person({
init: function( name, lastName ) {
this._super( name );
this.lastName = lastName;
},
sayName: function( text ) {
return this._super( text, this.lastName );
}
});
var improvedPerson = new ImprovedPerson( 'John', 'Doe' );
improvedPerson.sayName( 'To whom it may concern, I am ' );
// -> 'To whom it may concern, I am John Doe'
can.Observe.delegate observe.delegate( name, event, handler )
can.observe.delegate.js (Annotated source)
Use the can.Observe.delegate plugin to listen to change, set, add and remove on any direct, child or wildcard attribute:
// create an observable
var observe = new can.Observe({
foo: {
bar: 'Hello World',
baz: 'Hi there'
}
});
//listen to changes on a property
observe.delegate( 'foo.bar', 'change',
function( ev, prop, how, newVal, oldVal ) {
console.log( 'foo.bar has changed to ' + newVal );
});
observe.delegate( 'foo.*', 'change',
function( ev, prop, how, newVal, oldVal ) {
console.log( prop + ' has changed ' );
});
// change the property
observe.attr( 'foo.bar', 'Goodbye Cruel World' );
observe.attr( 'foo.baz', 'Bye you' );
can.Observe.setter
can.observe.setter.js (Annotated source)
With the can.Observe.setter plugin you can use attribute setter methods to process the value being set:
var Person = can.Observe({
setName: function( name ) {
return name.charAt( 0 ).toUpperCase() + name.slice( 1 );
}
});
var instance = new Person( { name: 'john' } );
// -> instance.name now is 'John'
instance.attr( 'name', 'doe' );
// -> instance.name now is 'Doe'
can.Observe.attributes
can.observe.attributes.js (Annotated source)
The can.Observe.attributes plugin allows you to specify attributes with type converters and serializers. Serializers make it handy when preparing your data to send to the server for JavaScript objects like dates or associations.
The following example creates a Birthday observe that defines a birthday attribute of type date. The serialize and convert properties allows you to implement your own conversion and serialization for each type.
var Birthday = new can.Observe({
attributes: {
birthday: 'date'
},
serialize : {
date : function( val, type ){
return val.getYear() +
"-" + (val.getMonth() + 1) +
"-" + val.getDate();
}
},
convert: {
// converts string to date
date : function( date ) {
if ( typeof date == 'string' ) {
//- Extracts dates formated 'YYYY-DD-MM'
var matches = raw.match( /(\d+)-(\d+)-(\d+)/ );
//- Parses to date object and returns
date = new Date( matches[ 1 ],
( +matches[ 2 ] ) - 1,
matches[ 3 ] );
}
return date;
}
}
}, {});
var brian = new Birthday();
// sets brian's birthday
brian.attr('birthday', '11-29-1983');
//- returns newly converted date object
var date = brian.attr('birthday');
//- returns { 'birthday': '11-29-1983' }
var seralizedObj = brian.serialize();
can.Observe.validations observe.validate( attribute, validator )
can.observe.validations.js (Annotated source)
can.Observe.validations adds validation to a can.Observe. Call the validate method in the init constructor with the attribute name and the validation function and then use errors to retrieve the error messages:
var Person = can.Model({
findAll : 'GET /people',
findOne : 'GET /people/{id}',
create : 'POST /people',
update : 'PUT /people/{id}',
destroy : 'DELETE /people/{id}',
init:function(){
this.validate('name', function( name ) {
if ( !name ) {
return 'Name can not be empty!';
}
})
}
}, { });
var john = new Person( { name : '' } );
john.errors();
// -> { name: [ 'Name can not be empty' ] }
can.Observe.backup observe.backup()
can.observe.backup.js (Annotated source)
You can backup and restore can.Observe data using the can.Observe.backup plugin. To backup the observe in its current state call backup. To revert it back to that state use restore:
var todo = new Todo( { name: 'do the dishes' } );
todo.backup();
todo.attr( 'name', 'Do not do the dishes' );
todo.isDirty(); // -> true
todo.restore();
todo.name // -> 'do the dishes'
can.Control.plugin
can.control.plugin.js (Annotated source)
can.Control.plugin registers a jQuery plugin function with a given pluginName that instantiates a can.Control. For example with this can.Control:
var Tabs = can.Control({
pluginName: 'tabs'
},{
init: function( element, options ) {},
update: function( options ) {},
activate: function( index ) {}
});
You can instantiate it by calling the plugin like this:
$( '.tabs' ).tabs();
Once created any subsequent plugin call will trigger update on your control with the options passed to the plugin. You can also call methods on the control instance like so:
// Call the activate method
$( '.tabs' ).tabs( 'activate', 0 );
Note that calling a method like this will return a jQuery object, not the actual return value. You can retrieve the controller instance directly using the .controls() or .control() helpers included in this plugin.
//- Returns an array of controllers on the match
var allControls = $( '.tabs' ).controls();
allControllers[ 0 ].activate( 0 );
//- Returns the first controller on the match
var control = $( '.tabs' ).control();
control.activate( 0 );
can.Control.view control.view( [ viewname ], [ data ] )
can.control.view.js (Annotated source)
can.Control.view renders a view from a URL in a views/controlname folder. If no viewname is supplied it uses the current action name. If no data is provided the control instance is passed to the view. Note that you have to set a name when creating the Control construct for view to work.
can.Control( 'Editor', {
click: function( el ) {
// renders with views/editor/click.ejs with the controller as data
this.element.html( this.view() );
// renders with views/editor/click.ejs with some data
this.element.html( this.view( { name: 'The todo' } ) );
// renders with views/editor/under.ejs
this.element.html( this.view( 'under', [ 1, 2 ] ) );
// renders with views/editor/under.micro
this.element.html( this.view( 'under.micro', [ 1, 2 ] ) );
// renders with views/shared/top.ejs
this.element.html( this.view( 'shared/top', { phrase: 'hi' } ) );
}
})
View modifiers
can.view.modifiers.js (Annotated source)
jQuery uses the modifiers after, append, before, html, prepend, replaceWith and text to alter the content of an element. This plugin allows you to render a can.View using these modifiers. For example, you can render a template from the todo/todos.ejs URL looking like this:
<% for( var i = 0; i < this.length; i++ ) { %>
<li><%= this[ i ].name %></li>
<% } %>
By calling the html modifier on an element like this:
$( '#todos' ).html( 'todo/todos.ejs', [
{ name: 'First Todo' },
{ name: 'Second Todo' }
]);
can.fixture
can.fixture.js (Annotated source)
can.fixture intercepts AJAX requests and simulates the response with a file or function. A static fixture intercepting a request to /todo and returning the content of fixtures/todo.json looks like this:
can.fixture("/todo", "fixtures/todo.json");
A dynamic fixture generates the response in a function and calls a callback with the result. URLs can be templated and the original parameters can be accessed in the fixture function. The following example intercepts any call to /todo/{id}.json and returns an object with the id that was passed and the current date in the updatedAt attribute:
can.fixture("PUT /todo/{id}.json",
function(original, respondWith){
respondWith({
id : original.id,
updatedAt : new Date().getTime()
});
})
can.fixture.make can be used to generate fixtures for can.Model to simulate findAll, findOne, create, update and destroy requests. For a Model like this:
var Todo = can.Model({
findAll : 'GET /todos',
findOne : 'GET /todos/{id}',
create : 'POST /todos',
update : 'PUT /todos/{id}',
destroy : 'DELETE /todos/{id}'
}, {});
The following example creates 100 Todos with the current counter as the id and a name and fixtures for creating, updating and deleting a Todo:
var store = can.fixture.make(100, function(i) {
return {
id : i,
name : 'Todo ' + i
}
});
can.fixture('GET /todos', store.findAll);
can.fixture('GET /todos/{id}', store.findOne);
can.fixture('POST /todos', store.create);
can.fixture('PUT /todos/{id}', store.update);
can.fixture('DELETE /todos/{id}', store.destroy);
Any fixture can be turned off by setting it to null or globally by setting can.fixture.on to false:
can.fixture("/todo.json", null);
// Turn off all fixtures
can.fixture.on = false;
Third Party Extensions and Plugins
Glues CanJS and jQueryMobile together.
A Ruby gem that provides CanJS (for jQuery) for your Rails 3.1+ application. Download the gem or learn more by visiting the rubygems.org site.
Examples
The following are some sweet apps built with CanJS. The wiki includes a list of tutorials and blog posts. And make sure you checkout CanJS Recipes
TodoMVC

TodoMVC implements the same Todo application in many different JavaScript MVC frameworks. There are two examples for CanJS, one for using CanJS with jQuery and the other for CanJS with jQuery and RequireJS:
CanPlay

A simple HTML5 video player application utilizing Popcorn.js. We set up two can.Control instances to control the Popcorn video as well as update the position of the player. This demo makes use of Templated Event Handlers by passing the Popcorn wrapped video element to the controller instances. The controllers then bind to events using the "{video}" templated event handler to listen and interact with the video element.
Srchr
Srchr searches several data sources for content and displays it to the user. It is built using the jQuery version of CanJS and is a great example of how to create dumb, isolated widgets that are loosely coupled to the rest of the application.

Contacts

A contacts manager application built to accompany the 3 part Nettuts+ “Diving into CanJS” tutorial series. This application is meant to introduce people to the core concepts of CanJS like Controls, Models, Views, Observables and Routing.
- Part 1: Tutorial | Demo | Download
- Part 2: Tutorial | Demo | Download
- Part 3: Tutorial | Demo | Download
iskon.Jumbo
Web application that allows you to upload and host your files in the cloud. Complete web client was built with CanJS. You can try it out at jumbo.iskon.hr

SnowHit.com
Snowhit is a unique website offering all services to skiers in one place. It contains 3D map, showing the location of lifts, ski runs, hotels etc. Best of all, it uses CanJS and Steal. You can try it out at snowhit.com

BootSwatchr
ThemeRoller for Twitter Bootstrap built on CanJS. Visit it at bootswatchr.com

Mindjet Tasks
Mindjet Tasks is task mangement application built using CanJS

Mindjet uses CanJS with Phonegap for Mobile Tasks on iPhone and Android

Get Help
There are several places you can go to ask questions or get help debugging problems.
Follow @canjs for updates, announcements and quick answers to your questions.
Forums
Visit the Forums for questions requiring more than 140 characters. CanJS has a thriving community that’s always eager to help out.
IRC
The CanJS IRC channel (#canjs on irc.freenode.net) is an awesome place to hang out with fellow CanJS developers and get your questions answered quickly.
Help Us Help You
Help the community help you by using the jsFiddle templates below. Just fork one of these templates and include the URL when you are asking for help.
Get Help from Bitovi
Bitovi (developers of CanJS) offers training and consulting for your team. They can also provide private one-on-one support staffed by their JavaScript/Ajax experts. Contact Bitovi if you’re interested.
Why CanJS
There are many libraries out there and it can be difficult to pick the one that’s right for you. In our humble opinion, the technology in CanJS is simply the best. It strikes a balance between:
- Size
- Ease of use
- Safety
- Speed
- Flexibility
The following are the reasons to use CanJS.
Size
On top of jQuery, CanJS is ~11k. Here’s some other frameworks for comparison:
- Backbone 8.97kb (with Underscore.js)
- Angular 24kb
- Knockout 13kb
- Ember 37kb
- Batman 15kb
Size is not everything. It really is what’s inside that counts. And that’s where we think CanJS really delivers a lot of bang for your buck.
Ease of use
This site highlights the most important features of CanJS. The library comes with thorough documentation and examples on the DoneJS documentation page. There are example apps for each library and several example for jQuery.
CanJS is also supported by Bitovi, formerly Jupiter Consulting. We are extremely active on the forums. And should the need arise, we provide support, training, and development.
Safety
Memory safety is really important, especially in long-lived, dynamic pages. CanJS combats this menace in two important and unique ways:
Controls that unbind event handlers auto-magically
Using templated event binding, Controls can listen to events on objects other than their element. For example, a tooltip listening to the window looks like:
var Tooltip = can.Control({
'{window} click': function( el, ev ) {
// hide only if we clicked outside the tooltip
if (!this.element.has( ev.target ) {
this.element.remove();
}
}
})
// create a Tooltip
var tooltipElement = $( '<div>INFO</div>' ).appendTo( el )
var tooltipInstance = new Tooltip( tooltipElement );
window now has a reference to the control which keeps the tooltipInstance and everything the tooltip instance might reference in memory. CanJS overwrites each library’s element remove functionality to destroy controls. Destroying a control unbinds all of its event handlers, removing any memory leaks auto-magically.
A model store that does not leak
It’s relatively common to load the same model instance multiple times on a single page. For example, an app might request todos due today and high-priority todos and render them like:
can.view( 'todosList.ejs', {
todaysTodos: Todo.findAll( { due: 'today' } ),
criticalTodos: Todo.findAll( { type: 'critical' } )
}).then(function( frag ) {
$( '#todos' ).html( frag );
})
todosList.ejs might look like:
<h2>Due Today</h2>
<% list( todaysTodos, function( todo ) { %>
<li <%= (el) -> el.data( 'todo', todo ) %>>
<%= todo.attr( 'name' ) %>
</li>
<% } ) %>
<h2>Critical Todos</h2>
<% list( criticalTodos, function( todo ) { %>
<li <%= (el) -> el.data( 'todo', todo ) %>>
<%= todo.attr( 'name' ) %>
</li>
<% } ) %>
If the result for of Todo.findAll( { due: 'today' } ) and Todo.findAll( { type: 'critical' } ) both share a todo instance like:
{ "id" : 5, "name" : "do dishes", "due" : "today", "type" : "critical" }
can.Model knows that this data represents the same todo and only creates one instance. This means that a single model instance is in both lists. By changing the todo’s name or destroying it, both lists will be changed.
However, model only stores these model instances while something is binding to them. Once nothing is bound to the model instance, they are removed from the store, freeing their memory for garbage collection.
Speed
The importance of performance is almost impossible to exaggerate. CanJS’s guts are highly optimized. See how:
Control initialization
can.Control pre-processes event handlers so binding is super fast. Compare initializing a can.Control, Backbone.View and Ember.View tabs widget:
This makes a big difference for page initialization if your site has lots of controls.
Live binding
CanJS’s live-binding is very fast. It only updates what’s necessary when it’s necessary. Compare its template rendering performance with three other common MVC frameworks:
In this test, CanJS has the fastest live-binding. Backbone and YUI are not doing live-binding, but CanJS is still the fastest.
In the popular counting circle example, Knockout visually appears the fastest, followed by CanJS.
This means that CanJS and Knockout are slightly faster at different things, but are likely tied for the fastest live-binding libraries.
Note: AngularJS throttles updates, which means it doesn’t fit well with these tests.
Model and view deferred support for parallel loading
Deferreds are simply awesome for handling asynchronous behavior. can.Model produces deferreds and can.view consumes them. With the view modifiers plugin, you can load a template and its data in parallel and render it into an element with:
$( '#todos' ).html( 'todos.ejs', Todo.findAll() );
Hot. You can do this without the view modifiers plugin like:
can.view( 'todos.ejs', Todo.findAll() ).then(function( frag ) {
$( '#todos' ).html( frag );
})
Opt-in data binding
Although can.EJS’s live-binding is super fast, setting up live data binding can be too slow in certain situations (like rendering a list of 1000 items). EJS’s live binding is opt-in. It only turns on if you are using the attr method. If the following template binds to a todo’s name …
<li> <%= todo.attr('name') %> </li>
… the following doesn’t setup live-binding and renders much faster …
<li> <%= todo.name %> </li>
Flexibility
Your library should not break-down as your application and organization grow and technologies change. CanJS’s flexibility will keep it valuable to you far into the future.
Supports multiple libraries and frameworks
Want to share code between a Zepto mobile app and a jQuery desktop app? No problem. CanJS code (especially models) can be shared across libraries, and so can skill sets! Working on a Dojo project today and a YUI one tomorrow? Don’t throw away all of your skills.
Designed for plugins
CanJS is extracted from JavaScriptMVC, but currently supports almost all of its MVC functionality through plugins. Start small, with its basic functionality, and extend it with plugins that handle things like:
- setters
- serialization / deserialization
- jQuery plugin generation
- validations
- calling super methods
These plugins have forced the core to be quite extendable, making 3rd party plugin development easy.
Engineered limber
CanJS’s tools are designed to work under almost every situation. Your server sends back XML with strange urls? That’s ok, overwrite can.Model.findAll or can.Model.models. Want some special teardown code for a control? Overwrite can.Control:destroy.
But our favorite bit of flexibility is how can.Observe works with nested data. It converts nested objects into observes automatically. For example:
var person = new can.Observe({
name: { first: 'Justin', last: 'Meyer' },
hobbies: [ 'programming', 'party rocking' ]
})
person.attr( 'name.first' ) //-> 'Justin'
person.attr( 'hobbies.0' ) //-> 'programming'
But most important, change events bubble, letting observes listen for when a nested property changes:
person.bind( 'change', function( ev, attr, how, newVal, oldVal ) {
attr //-> 'name.last'
how //-> 'set'
newVal //-> 'Meyer'
oldVal //-> 'Myer'
});
person.attr( 'name.last', 'Meyer' );
Developing CanJS
To develop CanJS, add features, etc, you first must install DoneJS. DoneJS is the parent project of CanJS. DoneJS is the 4.0 version of JavaSciptMVC. It has DocumentJS and Steal as submodules that are used to generate the documentation and build the CanJS downloads.
Installing
-
forkCanJS on github. -
Clone DoneJS with:
git clone git@github.com:bitovi/donejs -
Open the donejs folder’s .gitmodule file and change the URL of the
"can"submodule:url = git://github.com/bitovi/canjs.gitto your
forked URL likeurl = git://github.com/justinbmeyer/canjs.git -
Install all submodules by running
cd donejs git submodule update --init --recursiveDepending on your version of git, you might need to cd into each submodule and run
git checkout.
Developing
After installing CanJS and DoneJS, you’ll find CanJS’s files in a can folder. Within can, you’ll find a folder for each feature of CanJS: construct, control, model, etc.
Within each feature folder, for example construct, you’ll find a file for:
- the implementation of the feature -
construct.js - a demo of the feature -
construct.html - an overview documentation page -
construct.md - the feature’s tests -
construct_test.js - a page to run those tests -
qunit.html
Any plugins for that feature will be folders within the feature’s folder. Ex: proxy, super.
The can/test folder contains:
- a
test.htmlpage which tests jQuery by default. Load e.g.test.html?library=mootoolsto test another library. - a test page that tests all libraries and plugins:
index.html - a file that loads all feature tests:
can_test.js - a
plugin_test.htmlfile that tests all plugins
The can/util folder contains the compatibility layer for each library.
To develop CanJS:
- Edit the feature’s file.
- Add tests to the feature’s test file.
- Open the feature’s test page. Make sure it passes.
- Open
can/test/index.htmlin every browser to test everything. - Submit a pull request!
Documentation
To edit CanJS.us, installing CanJS and DoneJS is not necessary. Simply fork and edit the github pages’s index.md page online. Don’t forget to submit a pull request.
To edit the documentation at DoneJS.com:
-
install CanJS and DoneJS.
-
Edit the markdown and js files in the CanJS github repo. For example, to edit can.Control’s overview page, change can/control/control.md. To edit can.Control’s destroy method, change can/control/control.js where you find the
destroycomment. -
Generate the docs with:
./js site/scripts/doc.jsView them at
site/docs.html -
Submit a pull request.
Making a build
To make the CanJS builds, run:
./js can/build/build.js
It puts the downloads in can/dist/edge.
List of heroes
The following lists everyone who’s contributed something to CanJS. If we’ve forgotten you, please add yourself.
First, thanks to everyone who’s contributed to JavaScriptMVC and jQueryMX, and the people at Bitovi. You deserve heaps of recognition as CanJS is direcly based off JavaScriptMVC. This page is for contributors after CanJS’s launch. Thank you
- noah (1, 2, 3, 4, 5, 6)
- thecountofzero (1, 2, 3)
- roissard (1, 2)
- Michael Kebbekus (1)
- Daniel Salet (1)
- Daniel Franz (1)
- trickeyone (1)
- rjgotten (1)
- Amy Chen (1)
- Max Sadrieh (1)
- dimaf (1, 2)
- yusufsafak (1)
- verto (1)
- WearyMonkey (1)
- cohuman (1, 2)
- roelmonnens
- Craig Wickesser (1)
- Jeff Rose (1)
- Brad Momberger (1)
- Pablo Aguiar (1, 2, 3)
- David Schovanec (1, 2)
- Dan Connor (1)
- Jesse Baird (1)
for helping us with new features, bug fixes, and getting this out the door.
Change Log
1.1.5 ( Mar 25 2013 )
- change: Added Mustache.resolve to evaluate truthyness in a common way #333
- change: Fixed incorrect passing of context stacks with partials in Mustache #288
- change: Mustache does not correctly evaluate boolean value
- change: deparam: parse params with remaining ampersand
- change: Null objects within observes weren’t working properly with Mustache sections #307
- change: Mustache: Pass raw array data as the context instead of trying to resolve it #281
- change: Allow to pass an array index to removeAttr in Observe and Observe.List
- change: fixing no arg helpers
- change: Mustache interpolation issues using Observes inside of an attribute tag
- change: isObject is undefined
- change: Allow dots in Observe keys
- change: data-view-id being rendered in tag closing
- change: Execute startBatch callbacks
- change: HTML comments with either an element callback (EJS) or a helper (Mustache) rendered incorrectly
- change: Prevent leak from computes that have no bindings.
- change: Treat "–" as delimiter of empty element
- change: Fixing can.ajax with mootools
- change: CanJS tries to parse JSON-map which contains a dot in the key
- change: can.compute evaluations for the default Mustache handlers
- change: can.Mustache: Array of objects passed as context to partials, breaks data helper and rendering.
- change: adding greedy space to model url splitter
- change: Fix for numeric inputs not living binding
- change: Empty strings not handled properly
- change: can.Control event delegation problem
- change: fixing computes from converting type
- change: can.view with Deferreds doesn’t pass failures
- change: HTML comments trip EJS rendering
- change: can.Observe.prototype.each overrides Mustache helper.
- change: Any model with a "." in the key name will cause observe.js set() to throw and error](https://github.com/bitovi/canjs/issues/257)
- change: Item.List splice method does not convert inserted elements to Item type
- change: Mustache: DOM exception when applying certain block patterns
- change: Mustache: Interpolated values when iterating through an Observe.List fail if not surrounded by a DOM node
1.1.4 ( February 5, 2013 )
- fix: Haschange binding and route ready for all libraries
- fix: Get converters and .attr working the right way with nested objects
- fix: CanJS/ejs table+tbody rendering of a list gives nested tbody items
- fix: Mustache: Inconsistent treatment of function attributes
- fix: EJS renders “@@!!@@” instead of Model data when a Deferred is passed into can.view that takes a long time to resolve
- fix: Mustache: registered helpers do not create the context stack correctly
- fix: Mustache: only the current context is passed to partials, instead of the full stack
- fix: IE8 error when setting up observe list
- fix: Resetting a live-bound textarea changes its value to !!
- fix: hashchange binding still broken in mootools
- fix: can.Mustache - with context lost in nested sections
- fix: Enabled passing in helpers and partials to Mustache views, (1)
- fix: Make the resolved data available when using can.view
- fix: .attr method doesn’t merge nested objects
- fix: Live binding on observe.lists nested in an observe doesn’t work
- fix: Attributes/Converters Issue
- fix: Observe.List push/unshift doesn’t fire when sort comparator is set
- fix: Observe.List sort doesn’t use custom method passed
- fix: test&fix: null values crashing validations
- fix: EJS rendering null value
- fix: can.Observe sort plugin doesn’t trigger add events
- fix: Observe.List sort plugin erroring on item removal
- fix: Live binding on observe.lists nested in an observe doesn’t work
- fix: Observe.List sort doesn’t use custom method passed
- add: removeAttr can.Model attribute
- add: Calling destroy on non persisted model
- add: jQuery 1.9.x support
- add: Mustache Helpers that accept computes and return an element should work
1.1.3 ( December 11, 2012 )
- fix: Empty model displays !! as input values
- fix: Rendering models in tables produces different results than an equivalent observe
- fix:
dataMustache helper doesn’t parse attribute properly - fix: Partial Mustache views assume the right parent tag for live-binding
- fix: Mustache partials don’t parse properly
- fix: can.Control will fail to find $.event.special in a $.noConflict(true) scenario
- fix: Nameless view renderers should return document fragment
- fix: compute only updates once when a list’s contents are replaced
- add: Updated jQuery hashchange plugin
- add: Generate computes from an observe property
- add: Add can.Observe.List.prototype.replace
- add: Return resolved data models in view callback
1.1.2 ( November 28, 2012 )
- fix: Solve issue when stealing mustache templates -
can/view/mustachereturnscanobject now - fix: Controls shouldn’t bind to templated events that contain undefined values
- fix: Resetting a form changes input values to !!
- fix: Further AMD build improvements
- fix: Strange conversion of nested arrays to Observe.List
1.1.1 ( November 19, 2012 )
- fix: Solve issue when stealing mustache templates -
can/view/mustachereturnscanobject now - fix: Controls shouldn’t bind to templated events that contain undefined values
- fix: Resetting a form changes input values to !!
- fix: Further AMD build improvements
- fix: Strange conversion of nested arrays to Observe.List
1.1.0 ( November 13, 2012 )
-
add: AMD module support for each dependency (#46)
-
can.util
- Updated jQuery to 1.8.2
- Updated Zepto to 1.0rc1
- Updated YUI to 3.7.3
-
can.Mustache - add: Mustache/Handlebars support with Live Binding
-
can.view - Changed passing jQuery a node list instead of a fragment in View Modifiers
-
can.EJS
-
can.route - fix: hashchange binding with mootools broken
-
can.Control - add: control does not listen to touchmove event on controller itself
-
can.Observe
- add: List binding on .length of an object
- fix: validation error that incorrectly labels an attribute with a value of 0 as empty
- add: you can now pluralise event names to listen to matching events of that type (rather than firing a single event)
- add: compound sets now behave correctly
- fix: can.Observe.delegate sets wrong event.currentTarget
- add: ability to assign object as attribute type in can.Observe
-
can.Model
- fix: can.Model with attributes that are models gets corrupted when you call attr()
- add: missing dependency to can/model
- Moved can/model/elements to can/observe/elements and renamed
modelstoinstances - fix: can.Model.List doesn’t fire the change event on the expando properties
1.0.7 (June 25nd 2012)
-
can.compute - fix: a global collision with
can.Control. -
Removed globals - Thanks Daniel Franz!
1.0.6 (June 22nd 2012)
-
can.compute - add: a computed value type object that can be used to represent several observe properties or a single static value.
-
can.ejs - fix: problem with trailing text not being add: to template.
1.0.5 (June 2nd 2012)
-
can.model - add: ability to overwrite model crud operations by defining a
makeprefix: static function, such asmakeFindAll -
can.EJS - fix: problem with nested block statements.
-
can.each - add: optional third argument that defines the context for the iterator function.
-
can/util/function - add:
can.defermethod as an alias forsetTimeout(function(){}, 0). -
can.view - fix:
toIdso it will work with both older and newerstealversions.
1.0.4 (May 22nd 2012)
- fix: plugin build process
1.0.2 (May 20th 2012)
- fix: breaking namespace issue.
1.0.1 (May 18th 2012)
-
can.util - fix:
can.eachnow makes sure the collection being iterated is notundefined -
can.control - add: Redirect to another controller method using a string
-
can.model - fix: Model instances in model store will be updated when
findAllorfindOneretrieves updated instances fixes - fix: Static methods such asfindOneandfindAllcan now be rejected. Thanks roelmonnens! -
can.route
- add: Deliminating tokens now configurable
- fix: Current route wins if all else equal
1.0 (May 14st 2012)
-
Registers itself as an AMD module if
defineis in thewindow -
can.fixture - add: a fixture plugin
-
can.util - add: a util/function plugin
-
can.route
1.0 Beta 2 (April 21st 2012)
-
can.util
- change: reverse argument order of can.each
- change/fix: buildFragment returns non cached frag
- fix: zepto’s isEmptyObject was broke
-
can.observe
-
can.model
-
can.route
- fix: a host of bugs in libaries other than jQuery because can.route was not properly tested in other libraries.
- fix: can.param fixed in dojo,
1.0 Beta 1 (April 1st 2012)
Released!