There are a variety of ways to get CanJS. Read the using CanJS guide
for comprehensive list. For the following recipes, we will load CanJS
with a <script> tag pointed to CanJS's CDN.
Create a file called myapp.html and put the following in it to get started:
<script src="//code.jquery.com/jquery-1.10.1.min.js"></script>
<script src="//canjs.com/release/2.0.4/can.jquery.js"></script>
<script type="text/mustache" id="app-template">
//Template will go here
</script>
<script>
//Application code will go here
</script>
<!-- CanJS needs a place to put your application -->
<div id="my-app"></div>
To follow along with the other recipes, you can also use
this JSFiddle as a template.
You can also Download CanJS
or follow other tutorials to get
started, but for the rest of the examples below, we'll be using this
setup.
Say "Hello World"
In CanJS, content is displayed using templates. Instead of manually
changing elements in the DOM, you create a template and CanJS
automatically updates the page from the data in your application code.
Template
In the template section of myapp.html, put the following:
Templates are rendered with can.view, which takes two arguments: the first is the id of the template,
and the second is the data passed to the template (in this case,
an object with a message property).
Render the template with a message and insert it into the page with:
<script>
// Give message a value
var data = {message: "Hello World!"};
// Pass the id of the template and the data, containing our message to can.view
var frag = can.view("app-template", data);
//Load the DocumentFragment in the page
$("#my-app").html( frag )
</script>
frag is a DocumentFragment. A DocumentFragment
is a lightweight container of HTMLElements that can be inserted in the page quickly. They can be used
anywhere a normal HTMLElement is used.
Update Text in the Page
CanJS will update the page automatically when observable
data changes. To make observable data, pass raw data to can.Map,
can.List or can.compute like:
var data = new can.Map({message: "Hello World!"});
To change the message, use the attr() method of can.Map.
data.attr("message", "Goodbye World!")
When the button is clicked in the example below, the message is
changed with data.attr().
Inside the #each block, the attributes are scoped to individual
objects in the list of people.
To make changes to the list, use an Array method such as
push
or pop.
// adds a new person
people.push({firstname: "Paul", lastname: "Newman"})
// removes the last person
people.pop()
Show and Hide Elements
Instead of showing and hiding elements by changing the DOM
directly like:
$("h1").show()
$("h1").hide()
Make the template show or hide those elements when a value
changes.
{{#if visible}}
<h1>{{message}}</h1>
{{/if}}
When the button is clicked, change the observable value.
data.attr("visible", !data.attr("visible"))
Application State
Typically, it's not a good idea to mix view state and application data.
In the previous example, the message is application data, while the
visible property represents view state. In CanJS, state and data
should be separated using different observables.
var data = new can.Map({message: "Hello World!"}),
state = new can.Map({visible: true});
var frag = can.view("app-template", {
data: data,
state: state
})
As an application gets more complex, separating state from data
makes things more maintainable.
Create a Live Timestamp
This recipe demonstrates how to generate a 'live' timestamp
that displays in a human-readable format. This means handling
application state that changes over time, as well as making
information rendered in a template human-readable using a helper function.
First, we'll add a createdAt property to the data like:
var data = new can.Map({
message: "Hello World",
createdAt: new Date()
})
On the page, this should be displayed as a human readable
timestamp:
<h1>Hello World <i>created just now</i></h1>
and as time passes, the timestamp will update to:
<h1>Hello World <i>created a few seconds ago</i></h1>
and then update to "some seconds ago" and so forth.
To accomplish this, create a prettyDatemustache helper that converts
dates into a human readable format. A helper function is called from within the template where its result
will be displayed. The following calls prettyDate with an observable value of createdAt.
To call a function from a template, register it with can.view.
The third argument passed to can.view is an object with helper functions, so the dateHelper function
can be registered as prettyView.
var dateHelper = function ( date ) {
//helper function
}
var frag = can.view("app-template", data, {prettyDate: dateHelper});
In this helper, date is not a Date object, instead it is an observable can.compute that
contains the createdAt value. A can.compute is an observable that contains a single value. To read the value,
call the compute like you would any other function:
date() //-> Date
We need to compare date with the current time. The current time
will be represented by a compute:
var now = can.compute( new Date() )
As the current time changes, we update now with the new time. To change the value of a can.compute,
call it with its new value as an argument:
// update that property every second
setTimeout(function(){
now( new Date() );
setTimeout(arguments.callee, 1000);
},1000)
The prettyDate helper will read and compare the date and now compute to
get the time elapsed in seconds:
var timeElapsed = ( now() - date() ) / 1000
Using the timeElapsed, prettyDate returns human readable timestamps:
This recipe will introduce handling a click event using a can.Control.
Using a list of people like previous recipes, clicking any individual person's name
will remove that person from the list.
Previous examples have used jQuery's event handlers:
$("#push").click(function(){
//handle the event
})
CanJS provides a few different ways to respond to events. As well as
making application code simpler, using CanJS to handle events can help to
automatically prevent memory leaks.
To handle events, extend can.Control.
var PeopleList = can.Control.extend({
//behavior
});
You create a can.Control by calling it as a constructor function.
The first argument is the element the control will be created on.
The second argument is an object of options.
new PeopleList('#my-app', {people: people});
A can.Control handles events with functions declared with two arguments: an
element or list of elements (using a jQuery-style selector) and a specific event.
Below, this is 'li click', meaning when any li elements that are clicked the
function will be called to handle the click event.
var PeopleList = can.Control.extend({
init: function( element, options ){
this.people = new can.List(options.people);
this.element.html( can.view('app-template', {
//defines people in the template as the observable can.List
people: this.people
}));
},
'li click': function( li, event ){
//Handle the click event
}
}
When the constructor function is called and the can.Control
is instantiated:
The init method is called
An observable can.List is created from people
The list is rendered using can.view so when the list changes, so will the view
When the event handler for a click runs, it needs a way
to access the object associated with the li that was clicked.
With the data helper,
the element will retain a reference
to the object it is associated with (in this case, a person).
Finally, the event handler must be defined. In a can.Control,
an event handler function can be defined with a string containing
a selector and an event.
In this case, these are li and click, recpectively,
since we want to handle click events on each list item.
var PeopleList = can.Control.extend({
init: function(){
...
},
'li click': function( li, event ) {
var people = this.options.people;
var person = li.data('person');
var index = people.indexOf(person);
people.splice(index, 1);
}
});
When a user clicks a list item:
The function bound to li click is called
The object associated with that list item is accessed using the data helper
That 'person's data is removed from the observable list of people
The template updates automatically
As a reminder, though event handlers respond to actions on the page,
they should change application state or data (e.g. make a change to a can.Map)
rather than modifying the DOM directly (e.g. toggling a class).
This will update the page automatically, keeping code manageable.
This is one way to handle events. Others will be covered
in the following recipes while building widgets.
Build Widgets/UI Elements
Previous recipes have demonstrated how to change page content and introduced
event handling. The following recipes will introduce can.Component,
which allows for straightforward widget construction by packaging
template, state, and event handling code in one place.
While similar behavior can be accomplished with can.Control,
building a Component enables building reusable widgets using custom
HTML tags.
Create a Component
The previous recipe that displays a list of people can instead
be represented as a component.
<people></people>
By specifying people as the tag, a component is created wherever <people></people>
appears in a template.
can.Component.extend({
tag: 'people',
The scope object on a Component contains the component's state, data,
and behavior. Here, it specifies how to remove a person from the list:
scope: {
people: people,
remove: function( person ) {
var people = this.attr("people");
var index = people.indexOf(person);
people.splice(index, 1);
}
}
});
The template for the component itself is passed via the template
property. This can either be an external file or a string.
Each li uses can-click, which declares an event binding.
Here, remove inside the component's
scope will be called with the relevant people object
as an argument.
This behaves similarly to the can.Control from above.
However, the <people> tag can be used without having
any knowledge about the inner workings of the widget.
Using declarative HTML tags, a component can be used
without writing any javascript. The template, state,
and behavior are all combined into one Component.
Build a Tabs Widget
A tabs widget could be instantiated with the following HTML:
A designer that understands HTML can put together a template for a tabs
widget without understanding anything other than the syntax.
This is one of the most useful features of components.
Tabs Widget Behavior
Before implementing the component itself, we'll
define an observable view model--the scope object
of the UI element. This makes the code modular and easier
to manage (and also allows for unit testing).
In order to accurately represent a tabs widget,
a TabsViewModel needs:
An observable list of panels
A state variable with the active panel
Helper methods to add, remove, and activate panels
Since TabsViewModel is a can.Map, the panels property is
automatically converted to a can.List.
The active property references the panel object
that should currently be displayed.
var TabsViewModel = can.Map.extend({
panels: [],
active: null,
addPanel: function( panel ){
var panels = this.attr("panels");
panels.push(panel);
panel.attr("visible", false);
//activate panel if it is the first one
if ( panels.attr("length") === 1 ){
this.activate( panel );
}
},
removePanel: function( panel ){
var panels = this.attr("panels");
var index = panels.indexOf(panel);
panels.splice(index, 1);
//activate a new panel if panel being removed was the active panel
if( this.attr("active") === panel ){
panels.attr("length") ? this.activate(panels[0]) : this.attr("active", null)
}
},
activate: function( panel ){
var active = this.attr("active")
if( active !== panel ){
active && active.attr("visible", false);
this.attr("active", panel.attr("visible", true));
}
}
})
Tabs Widget Component
Now that the view model is defined, making a component is simply
a matter of defining the way the tabs widget is displayed.
The template for a tabs component needs a list of panel titles
that will activate that panel when clicked. By calling activate
with a panel as the argument, the properties of the panel can
be manipulated. By changing the visible property of a panel,
a template can be used to display or hide the panel accordingly.
For this component, our template should look something like this:
A designer can create a tabs component with panel components inside it.
The template object on the tabs component's scope needs to be able to render
the content that is inside of the <tabs> tag. To do this, we simply use the
<content> tag, which will render everything within the component's tags:
The tabs component contains panels, which are also defined
as components. The tabs template contains the logic for whether
the panel is visible (visible is controlled by the tabs
component's activate method).
Each panel's scope contains a title, which should be
taken from the title attribute in the <panel> tag.
If you want to set the string value of a Component's
attribute as a scope variable, use @'.
In addition to the scope property, a component has an
events property.
This events property uses a can.Control instantiated inside
the component to handle events.
Since we defined behavior for adding panels on the parent
tabs component, we should use this method whenever a panel
is inserted into the page (and an inserted event is triggered).
To add the panel to the tabs component's scope, we call the
addPanel method by accessing the parent scope with this.element.parent().scope():
With this component, any time a <tabs> element with
<panel> elements is put in a page, a tabs widget will
automatically be created. This allows application behavior
and design to be compartmentalized from each other.
Build an Application with Data from a Server
In CanJS, can.Model adds functionality to can.Map to
work with data on a server. It enables you to:
Get and modify data from a server
Listen to changes made to the data on the server
Unify service data with other objects in your application
can.Model allows you to access data from a server
easily:
var Todo = can.Model.extend({
findAll: 'GET /todos',
findOne: 'GET /todos/{id}',
create: 'POST /todos',
update: 'PUT /todos/{id}',
destroy: 'DELETE /todos/{id}'
},{});
Using any server with a REST interface,
can.Model enables create, read, update, and destroy functionality.
Create a Chat Application
To put together a chat application, we'll use two methods
from can.Model to fetch the messages and create new ones:
In a chat component's scope, we will use the Message model to
save new messages and observe changes to the Model.
new Message.List({}) is a shortcut to perform
the findAll operation on a can.Model and
return a can.List.
...
scope: {
messages: new Message.List({}),
newMessage: ""
...
The tabs Component used can-click to listen for click events.
Since this chat application uses a <form> for sending messages, we'll use
can-submit to specify an event handler.
There's one more helper used in the template: can-value.
This automatically two-way binds the value of an input field to an observable
property on the scope of the component (in this case, newMessage).
When submitMessage is called, a new Message is created
with new Message(). Since can-value was declared on the input element, newMessage will
always be the current text in the input field.
The body of the message is fetched from
the Component's newMessage attribute when a user submits the form.
To save the new message to the server, call save().
submitMessage: function(scope, el, ev){
ev.preventDefault();
new Message({body: this.attr("newMessage")}).save();
this.attr("newMessage", "");
}
Finally, when a new Message is created, the messages list
must be updated.
There are two ways that messages are added: from the current user,
or from another user. In the next section, we demonstrate how to use
socket.io to update the Message model with messages
from other users in real time. Binding to the created event for all
messages allows us to create a single entry point that pushes new messages
to the scope, regardless of where those messages are from.
When the chat Component is loaded, messages are loaded from the server
using can.Model and new Message.List({}). When a new message is
submitted:
submitMessage is called via the event handler bound by the can-submit attribute
a new Message is created and saved to the server
'{Message} created' detects this change and adds the new message to messages
The template is automatically updated since messages is an observable can.List
Add real-time functionality
This example uses socket.io
to enable real-time functionality. This guide won't go
into detail on how to use socket.io, but for real-time
chat the application needs two more things.
When a message is created on another chat client, socket.io
will notify this client by triggering the message-created event,
wich will render the new message in the page by adding it to the
Message model.
var socket = io.connect(myServerUrl)
socket.on('message-created', function(message){
new Message(message).created();
})
To keep the created event from firing
twice, we modify the create function in the model.
If there was simply a return statement, Model would
create and fire a create event, which socket is already
doing. By returning a Deferred, we prevent firing of
one of these events.
Get Started
There are a variety of ways to get CanJS. Read the using CanJS guide for comprehensive list. For the following recipes, we will load CanJS with a
<script>
tag pointed to CanJS's CDN.Create a file called
myapp.html
and put the following in it to get started:To follow along with the other recipes, you can also use this JSFiddle as a template.
You can also Download CanJS or follow other tutorials to get started, but for the rest of the examples below, we'll be using this setup.
Say "Hello World"
In CanJS, content is displayed using templates. Instead of manually changing elements in the DOM, you create a template and CanJS automatically updates the page from the data in your application code.
Template
In the template section of
myapp.html
, put the following:This template displays the value of
message
.Pass message to the Template
Templates are rendered with can.view, which takes two arguments: the first is the
id
of the template, and the second is the data passed to the template (in this case, an object with amessage
property).Render the template with a
message
and insert it into the page with:Update Text in the Page
CanJS will update the page automatically when observable data changes. To make observable data, pass raw data to can.Map, can.List or can.compute like:
To change the message, use the attr() method of
can.Map
.When the button is clicked in the example below, the message is changed with
data.attr()
.Show a List in a Template
To make an Array observable, pass it to can.List.
To show a list of data within a mustache template, use the
#each
operator.Inside the
#each
block, the attributes are scoped to individual objects in the list ofpeople
.To make changes to the list, use an Array method such as push or pop.
Show and Hide Elements
Instead of showing and hiding elements by changing the DOM directly like:
Make the template show or hide those elements when a value changes.
When the button is clicked, change the observable value.
Application State
Typically, it's not a good idea to mix view state and application data. In the previous example, the
message
is application data, while thevisible
property represents view state. In CanJS, state and data should be separated using different observables.As an application gets more complex, separating state from data makes things more maintainable.
Create a Live Timestamp
This recipe demonstrates how to generate a 'live' timestamp that displays in a human-readable format. This means handling application state that changes over time, as well as making information rendered in a template human-readable using a helper function.
First, we'll add a
createdAt
property to the data like:On the page, this should be displayed as a human readable timestamp:
and as time passes, the timestamp will update to:
and then update to "some seconds ago" and so forth.
To accomplish this, create a
prettyDate
mustache helper that converts dates into a human readable format. A helper function is called from within the template where its result will be displayed. The following callsprettyDate
with an observable value ofcreatedAt
.To call a function from a template, register it with
can.view
. The third argument passed tocan.view
is an object with helper functions, so thedateHelper
function can be registered asprettyView
.In this helper,
date
is not a Date object, instead it is an observable can.compute that contains thecreatedAt
value. Acan.compute
is an observable that contains a single value. To read the value, call the compute like you would any other function:We need to compare
date
with the current time. The current time will be represented by a compute:As the current time changes, we update
now
with the new time. To change the value of acan.compute
, call it with its new value as an argument:The
prettyDate
helper will read and compare thedate
andnow
compute to get the time elapsed in seconds:Using the
timeElapsed
,prettyDate
returns human readable timestamps:Handle User Interaction
When a user does something, such as clicking, an
event
occurs. Event handlers specify how JavaScript should respond to an event.This recipe will introduce handling a click event using a
can.Control
. Using a list of people like previous recipes, clicking any individual person's name will remove that person from the list.Previous examples have used jQuery's event handlers:
CanJS provides a few different ways to respond to events. As well as making application code simpler, using CanJS to handle events can help to automatically prevent memory leaks.
To handle events, extend
can.Control
.You create a
can.Control
by calling it as a constructor function. The first argument is the element the control will be created on. The second argument is an object of options.A
can.Control
handles events with functions declared with two arguments: an element or list of elements (using a jQuery-style selector) and a specific event. Below, this is 'li click', meaning when anyli
elements that areclicked
the function will be called to handle the click event.When the constructor function is called and the
can.Control
is instantiated:The
init
method is calledAn observable
can.List
is created frompeople
The list is rendered using
can.view
so when the list changes, so will the viewWhen the event handler for a
click
runs, it needs a way to access the object associated with theli
that was clicked. With thedata
helper, the element will retain a reference to the object it is associated with (in this case, aperson
).Finally, the event handler must be defined. In a
can.Control
, an event handler function can be defined with a string containing a selector and an event. In this case, these areli
andclick
, recpectively, since we want to handle click events on each list item.When a user clicks a list item:
li click
is calleddata
helperpeople
As a reminder, though event handlers respond to actions on the page, they should change application state or data (e.g. make a change to a
can.Map
) rather than modifying the DOM directly (e.g. toggling a class). This will update the page automatically, keeping code manageable.This is one way to handle events. Others will be covered in the following recipes while building widgets.
Build Widgets/UI Elements
Previous recipes have demonstrated how to change page content and introduced event handling. The following recipes will introduce
can.Component
, which allows for straightforward widget construction by packaging template, state, and event handling code in one place.While similar behavior can be accomplished with
can.Control
, building a Component enables building reusable widgets using custom HTML tags.Create a Component
The previous recipe that displays a list of people can instead be represented as a component.
By specifying
people
as the tag, a component is created wherever<people></people>
appears in a template.The
scope
object on aComponent
contains the component's state, data, and behavior. Here, it specifies how toremove
a person from the list:The template for the component itself is passed via the
template
property. This can either be an external file or a string. Eachli
usescan-click
, which declares an event binding. Here,remove
inside the component's scope will be called with the relevantpeople
object as an argument.This behaves similarly to the
can.Control
from above. However, the<people>
tag can be used without having any knowledge about the inner workings of the widget. Using declarative HTML tags, a component can be used without writing any javascript. The template, state, and behavior are all combined into one Component.Build a Tabs Widget
A tabs widget could be instantiated with the following HTML:
A designer that understands HTML can put together a template for a
tabs
widget without understanding anything other than the syntax. This is one of the most useful features of components.Tabs Widget Behavior
Before implementing the component itself, we'll define an observable view model--the
scope
object of the UI element. This makes the code modular and easier to manage (and also allows for unit testing).In order to accurately represent a tabs widget, a
TabsViewModel
needs:Since TabsViewModel is a
can.Map
, thepanels
property is automatically converted to acan.List
. Theactive
property references thepanel
object that should currently be displayed.Tabs Widget Component
Now that the view model is defined, making a component is simply a matter of defining the way the tabs widget is displayed.
The template for a
tabs
component needs a list of panel titles that willactivate
that panel when clicked. By callingactivate
with a panel as the argument, the properties of thepanel
can be manipulated. By changing thevisible
property of a panel, a template can be used to display or hide the panel accordingly.For this component, our template should look something like this:
A designer can create a
tabs
component withpanel
components inside it. Thetemplate
object on the tabs component's scope needs to be able to render the content that is inside of the<tabs>
tag. To do this, we simply use the<content>
tag, which will render everything within the component's tags:The
tabs
component contains panels, which are also defined as components. The tabs template contains the logic for whether the panel is visible (visible
is controlled by the tabs component'sactivate
method).Each panel's
scope
contains a title, which should be taken from thetitle
attribute in the<panel>
tag. If you want to set the string value of a Component's attribute as ascope
variable, use@'
.In addition to the
scope
property, a component has anevents
property. Thisevents
property uses acan.Control
instantiated inside the component to handle events.Since we defined behavior for adding panels on the parent
tabs
component, we should use this method whenever apanel
is inserted into the page (and aninserted
event is triggered). To add the panel to thetabs
component's scope, we call theaddPanel
method by accessing the parent scope withthis.element.parent().scope()
:With this component, any time a
<tabs>
element with<panel>
elements is put in a page, a tabs widget will automatically be created. This allows application behavior and design to be compartmentalized from each other.Build an Application with Data from a Server
In CanJS,
can.Model
adds functionality tocan.Map
to work with data on a server. It enables you to:can.Model
allows you to access data from a server easily:Using any server with a REST interface,
can.Model
enables create, read, update, and destroy functionality.Create a Chat Application
To put together a chat application, we'll use two methods from
can.Model
to fetch the messages and create new ones:In a chat component's scope, we will use the
Message
model to save new messages and observe changes to the Model.new Message.List({})
is a shortcut to perform thefindAll
operation on acan.Model
and return acan.List
.The tabs Component used
can-click
to listen for click events. Since this chat application uses a<form>
for sending messages, we'll usecan-submit
to specify an event handler.There's one more helper used in the template:
can-value
. This automatically two-way binds the value of an input field to an observable property on thescope
of the component (in this case,newMessage
).When
submitMessage
is called, a newMessage
is created withnew Message()
. Sincecan-value
was declared on theinput
element,newMessage
will always be the current text in theinput
field. The body of the message is fetched from the Component'snewMessage
attribute when a user submits the form.To save the new message to the server, call
save()
.Finally, when a new
Message
is created, themessages
list must be updated.There are two ways that messages are added: from the current user, or from another user. In the next section, we demonstrate how to use socket.io to update the
Message
model with messages from other users in real time. Binding to thecreated
event for all messages allows us to create a single entry point that pushes new messages to thescope
, regardless of where those messages are from.When the chat Component is loaded, messages are loaded from the server using
can.Model
andnew Message.List({})
. When a new message is submitted:submitMessage
is called via the event handler bound by thecan-submit
attributeMessage
is created and saved to the server'{Message} created'
detects this change and adds the new message tomessages
messages
is an observablecan.List
Add real-time functionality
This example uses socket.io to enable real-time functionality. This guide won't go into detail on how to use
socket.io
, but for real-time chat the application needs two more things.When a message is created on another chat client,
socket.io
will notify this client by triggering themessage-created
event, wich will render the new message in the page by adding it to theMessage
model.To keep the
created
event from firing twice, we modify thecreate
function in the model. If there was simply areturn
statement,Model
would create and fire acreate
event, whichsocket
is already doing. By returning aDeferred
, we prevent firing of one of these events.Add my own recipe
To add your own recipe, simply edit this file. To help create a JSFiddle, we've created the following fiddles you can fork:
Request a Recipe
To request a new recipe or vote on an upcoming one, submit an issue to the
canjs.com
respository on GitHub.