Notice the magic tags? Those are things that look like <% %> and
<%= %>. Code between <% %> is run and the return value of code
between <%= %> is inserted into the page.
Next, create a teacher and use can.view to render the template:
<div id='teachers'>
<h2 class='good'>
Mr. Smith
</h2>
<ul>
<li>Suzy</li>
<li>Payal</li>
<li>Curtis</li>
<li>Alexis</li>
</ul>
</div>
This is nice, but what if we change properties of the teacher?
Basic Live Binding Example
EJS sets up live templating binding when a can.Observe's properties are read
via attr within a magic tag. To make this template
respond to changes in the teacher data, first rewrite the template
to use the attr method to read properties and list( observeList, cb(item, i) )
to iterate through a list like:
Note: The end of this page discusses why using list is
helpful, but it does nothing fancy.
Next, turn your teacher into a new can.Observe(object) and pass
that to can.view:
var teacher = new can.Observe({
name : "Mr. Smith",
grade : "a",
students : [
{name : "Suzy"},
{name : "Payal"},
{name : "Curtis"},
{name : "Alexis"}
]
});
document.getElementById('teacher')
.appendChild( can.view("teacherEJS", teacher) );
Finally, update some properties of teacher and slap your
head with disbelief ...
teacher.attr('name',"Prof. Snape")
teacher.attr('grade','f+')
teacher.attr('students').push({
name : "Harry Potter"
})
... but don't slap it too hard, you'll need it for building awesome apps.
Demo
The following demo shows an EJS template being rendered with observable data.
It demonstrates live binding to attributes. The template and all data properties
are editable, so experiment!
## Magic Tags
EJS uses 5 types of tags:
<%= CODE %> - Runs JS Code and writes the escaped result into the result of the template.
The following results in the user seeing "my favorite element is <blink>BLINK<blink>" and not
.
<div>my favorite element is <%= '<blink>BLINK</blink>' %>.</div>
<%== CODE %> - Runs JS Code 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>
<%% CODE %> - Writes <% CODE %> to the result of the template. This is very useful for generators.
<%%= 'hello world' %>
<%# CODE %> - Used for comments. This does nothing.
<%# 'hello world' %>
Live Binding
EJS allows live binding by wrapping magic tag content within a function. When attr() is called
to update an observable object, these functions are executed to return the new value.
// Suppose an observable "foo":
var foo = new can.Observe({
bar: 'baz'
});
// Suppose also, the above observable is passed to our view:
<%= foo.attr('bar') %>
// EJS locates the magic tag and turns the above into:
function() { return foo.attr('bar'); }
// As "foo" is updated using attr(), this function is called again to
// render the view with the new value.
This means that each function tag has a closure will reference variables in it's
parent functions. This can cause problems if you don't understand closures in
JavaScript. For example, the following binding does not work:
<% for(var i =0; i < items.attr('length'); i++){ %>
<li><%= items[i].attr('name') %></li>
<% } %>
This is because it gets turned into:
<% for(var i =0; i < items.attr('length'); i++){ %>
LIVEBIND( function() { return items[i].attr('name') )
<% } %>
When the wrapping function is called again, i will
not be the index of the item, but instead be items.length.
The can.Observe.List.each method on all observable lists should be used to iterate through it:
Once you get the hang of how EJS works, it makes live-binding of complex
calculations possible. The following extends a can.Model.List to suppot a completed method that
returns the total number of completed items in the list. It can be used in a template like:
completed listens on changes to the list (via this.attr('length')) and
each item's 'completed' property. EJS keeps track of which observe/attribute pairs are called
by .complete(). If they change, EJS will automatically unbind.
Adding a "completed" helper function to the todo model list to return the number of completed todos:
Note: The object passed into the view becomes "this" within the view template.
var todos = Todo.findAll({}); //returns a can.Model.List
can.view('//todo/views/init.ejs', todos)
Element Callbacks
If a function is returned by the <%= %> or <%== %> magic tags within an element’s tag like:
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:
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:
<% todos.each( function( todo ) { %>
<li <%= (el) -> can.data( el, 'todo', todo ) %>>
<%= todo.attr( 'name' ) %>
</li>
<% } ) %>
Basic Example
The following renders a Teacher's name and students into an element. First, create a teacher template in a script tag like:
Notice the magic tags? Those are things that look like
<% %>
and<%= %>
. Code between<% %>
is run and the return value of code between<%= %>
is inserted into the page.Next, create a teacher and use can.view to render the template:
This results in HTML like:
This is nice, but what if we change properties of the teacher?
Basic Live Binding Example
EJS sets up live templating binding when a can.Observe's properties are read via attr within a magic tag. To make this template respond to changes in the teacher data, first rewrite the template to use the attr method to read properties and
list( observeList, cb(item, i) )
to iterate through a list like:Note: The end of this page discusses why using
list
is helpful, but it does nothing fancy.Next, turn your teacher into a
new can.Observe(object)
and pass that tocan.view
:Finally, update some properties of teacher and slap your head with disbelief ...
... but don't slap it too hard, you'll need it for building awesome apps.
Demo
The following demo shows an EJS template being rendered with observable data. It demonstrates live binding to attributes. The template and all data properties are editable, so experiment!
## Magic TagsEJS uses 5 types of tags:
<%= CODE %>
- Runs JS Code and writes the escaped result into the result of the template.The following results in the user seeing "my favorite element is <blink>BLINK<blink>" and not .
<%== CODE %>
- Runs JS Code 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.<%% CODE %>
- Writes <% CODE %> to the result of the template. This is very useful for generators.<%# CODE %>
- Used for comments. This does nothing.Live Binding
EJS allows live binding by wrapping magic tag content within a function. When
attr()
is called to update an observable object, these functions are executed to return the new value.This means that each function tag has a closure will reference variables in it's parent functions. This can cause problems if you don't understand closures in JavaScript. For example, the following binding does not work:
This is because it gets turned into:
When the wrapping function is called again,
i
will not be the index of the item, but instead be items.length.The can.Observe.List.each method on all observable lists should be used to iterate through it:
Advanced Live Binding
Once you get the hang of how EJS works, it makes live-binding of complex calculations possible. The following extends a can.Model.List to suppot a
completed
method that returns the total number of completed items in the list. It can be used in a template like:And implemented like:
completed
listens on changes to the list (viathis.attr('length')
) and each item's'completed'
property. EJS keeps track of which observe/attribute pairs are called by.complete()
. If they change, EJS will automatically unbind.Adding a "completed" helper function to the todo model list to return the number of completed todos:
Note: The object passed into the view becomes "this" within the view template.
Element Callbacks
If a function is returned by the
<%= %>
or<%== %>
magic tags within an element’s tag like: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:
This technique is commonly used to add data, especially model instances, to an element like:
jQuery’s
el.data( NAME, data )
adds data to an element. If your library does not support this, can provides it ascan.data( NodeList, NAME, data )
. Rewrite the above example as: