Canjs View

canjs

What is the purpose of can.view?

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

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

CanJS comes with the Embedded JS template engine built in, but you can use any engine you prefer. EJS is incompatable with can.Component and should be avoided for new projects. It will still be maintained up to 3.0 and potentially after. Projects using EJS should consider switching to can.stache.

can.view( idOrUrl, data[, helpers] )

Loads a template, renders it with data and helper functions and returns the HTML of the template within a documentFragment.

var frag = can.view(
    "/contact.ejs",
    {first: "Justin", last: "Meyer"},
    {
        fullName: function(first, last){
            return first +" "+ last
        }
    }
);

document.getElementById('contacts').appendChild(frag)

Why is can.EJS deprecated?

EJS is incompatable with can.Component and should be avoided for new projects. It will still be maintained up to 3.0 and potentially after. Projects using EJS should consider switching to can.stache.

How many parameters does can.view take?

The can.view function can take up to 3 parameters. The first parameter represent the template. This can be the path to the template, or an ID of a script tag. The second parameter is an JSON which can contain multiple properties. This JSON is then passed to the template engine. If this JSON contains a property name 'category', inside the template, we can just refer to this property by its name 'category'.

The third parameter, if present, contains the helper functions.

When 3 parameters are specified, the can.view function returns a document fragment. When only one parameter is specified, the can.view function registers or loads a template and returns a renderer function that can be used to later render the template with data and helpers.

var renderer = can.view("/contact.ejs");
var frag = renderer(
    {first: "Justin", last: "Meyer"},
    {
        fullName: function(first, last){
            return first +" "+ last
        }
    }
);

document.getElementById('contacts').appendChild(frag)

What is the purpose of the first parameter that the can.view function accept?

idOrUrl {String | Object}: The URL of a template or the id of a template embedded in a script tag or an object containing a url property for the URL to load and an engine property for the view engine (mustache or ejs) if it can't be infered from the file extensions or script tag type.

How can we use a template WITHOUT using script tag?

You can load a template without registering it first (or including it on the page) by giving the URL to can.view:

Todo.findAll({}, function(todos) {
    $('#nav').html(can.view('todos/todos.ejs', todos))
});

How can we use embedded script tag template?

<script type=‘text/ejs’ id=‘messageEJS’>
    <h1><%= message %></h1>
</script>

can.view(‘messageEJS’, {
    message: ‘Hello World’
});
<script type="text/ejs" id="todoList">
    <% can.each(this, function(val, key) { %>
        <li><%= val.attr('description') %></li>
    <% }); %>
</script>

Todo.findAll({}, function(todos) {
    $('#nav').html(can.view('todoList', todos))
});

What happens if the second parameter passed to can.view contains a deferred?

If the second parameter you pass to can.view contains Deferreds, can.view will instead return a Deferred that resolves to the documentFragment containing the populated template after all the deferreds have resolved.

This aspect is most useful because [Model] methods like findAll return a Deferred. This allows you to load a template, retrieve one or more Models, and then render the resulting documentFragment after everything has been loaded:

can.view('todos.ejs', {
    todos: Todo.findAll().
    user: User.findOne({id: 5})
}).then(function(fragment) {
    document.getElementById('todos').appendChild(fragment);
});

In the above code, after the result of findAll and findOne came back from the server, the code in the todos.ejs template is evaluated, and the result of this evaluation (which is a document fragment) is passed to the function that was specified to the then function.

What are the differences between can.view and can.view.render?

To render to a string instead of a documentFragment, use can.view.render. This is mainly used to nest templates inside of other templates:

<% can.each(todos, function(todo, key) { %>
    <li><%== can.view.render('todos.ejs', todo); %></li>
<% }) %>

Why do live binding not work inside a loop and what can we do to make it work?

Live binding will automatically update your EJS templates in the DOM whenever the data they are populated with changes. To do this, populate your templates with Observes and use attr to read properties. In this template, using attr sets up live binding on the description property of todo:

<li><%= todo.attr('description') %></li>

If you change the Todo's description, the template's output will automatically update:

todo.attr('description', 'Clean up the bathroom.');

Live binding works by wrapping the code inside the magic tags with a function to call when the attributes inside the magic tags change. This means that a template like this will not work:

<% for(var i = 0; i < todos.length; ++i) { %>
    <li><%= todos[i].attr('name') %></li>
<% } %>

This will not work because when the function wrapping todos[i].attr('name') is called, i will still be 3 (as that is what i is set to after the loop is run). You can fix this by using a closure and the each method of Observes:

<% todos.each(function() { %>
    <li><%= todo.attr('name') %></li>
<% }); %>

each will also watch the length of the list it is passed, so elements are added or removed from it, it will update the output of the template.

What happens if the code inside <%= %> or <%== %> evaluated to a function?

If the code inside <%= %> or <%== %> evaluates to a function, the function will be called back with the element it's inside as its first argument. This is useful to initialize functionality on an element within the template, like starting an element hidden:

<img src="surprise.gif" <%= function(element) { element.style.display = 'none'; } %>/>

This is so common that EJS also supports ECMAScript 5 arrow functions that get passed a library-wrapped NodeList containing the element. Because we are using jQuery, the example above can be more simply written like this:

<img src="surprise.gif" <%= (el) -> el.hide() %>/>

You can use this functionality to easily attach data to an element. A common reason to do this is to attach a Model to the element that represents it:

<% todos.each(function(todo) { %>
    <li <%= (el) -> can.data(el, 'todo', todo) %>>
        <%= todo.attr('description') %>
    </li>
<% }) %>

How can we create a template from a string?

Create a template for a given id programmatically using can.view.<engine>(id, template):

can.view.ejs('myViewEJS', '<h2><%= message %></h2>');
can.view('myViewEJS', { message : 'Hello EJS' });
// -> <h2>Hello EJS</h2>

It is also possible to get a nameless renderer function when creating a template from a string:

var renderer = can.view.ejs('<strong><%= message %></strong>');
renderer({
    message : 'Message form EJS'
}); // -> <strong>Message from EJS</strong>

renderer = can.mustache('<strong>{{message}}</strong>');
renderer({
    message : 'Message form Mustache'
}); // -> <strong>Message from Mustache</strong>

How can we render to strings and sub-templates?

To render to a string, use can.view.render(idOrUrl, data) like:

var str = can.view.render("/templates/recipe.ejs",{recipe: recipe});

To convert that rendered string into a live documentFragment, use the can.frag function. To render a can.ejs sub-template within another template, use render like:

<% $.each(recipes, function(i, recipe){ %>
    <li><%== can.view.render("/templates/recipe.ejs",{
        recipe: recipe
        }) %>
    </li>
<% }) %>

What happens if we pass a deferred to the can.view function?

If you pass deferreds to can.view it will wait until all deferreds resolve before rendering the view. This makes it a one-liner to make a request and use the result to render a template. The following makes a request for todos in parallel with the todos.ejs template. Once todos and template have been loaded, it with render the view with the todos:

can.view('recipes', Todo.findAll() , function(frag){
    document.getElementById('recipes').appendChild(frag)
})

What is the purpose of the the can.view.element.after method?

elements.after(oldElements, newFrag)

Inserts newFrag after oldElements.

  • oldElements {Array<HTMLElement>}
  • newFrag {DocumentFragment}

What is the purpose of the can.view.element.replace method?

elements.replace(oldElements, newFrag)

Replaces oldElements with newFrag.

  • oldElements {Array<HTMLElement>}
  • newFrag {DocumentFragment}

What is the purpose of the can.view.Options method?

Create a helper lookup node for keys. Options are where mustache helpers, partials, local tags, and other non-data objects are found.

new can.view.Options(options, [parent])
  1. options {Object}: An object with at least one of the following properties:
    1. helpers - Mustache helpers will be found within this object.
    2. partials - Mustache partials will be found within this object.
    3. tags - Local tag hookups will be found within this object.
    4. If none of these properties are found, the object is assumed to be a helpers object.
  2. parent {can.view.Options}: The parent options object. If a key value is not found in the current options object, it will then look in the parent scope.

This function returns an options instance. The can.view.Options is rarely used directly. However, they are indirectly created in several places:

  1. The helpers argument of can.view
  2. can.Component.prototype.helpers
  3. A mustache section renderer's helpers argument.

And can.view.Options are provided several places:

  1. A Mustache helper options's options property.
  2. An attribute callback's data's options property.
  3. A tag callback's data's options property.

Options works just like can.view.Scope except it contains references to local non-data values like:

  1. mustache helpers
  2. partial templates
  3. tag hookups

When a mustache template is rendered, these can be specified like:

var options = {
    helpers: {
        isSelected: function( helperOptions ){ ... }
    },
    partials: {
        person: can.view("person")
    },
    tags: {
        people: function(el, tagData){ ... }
    }
}
can.view("people.mustache", data, options)

If no helpers, partials or tags properties are found, options are assumed to be helpers. The following would pass a loal isSelected to "people.mustache":

can.view("people.mustache", data, {
    isSelected: function( helperOptions ){ ... }
})

What is the purpose of the can.view.hook method?

Create a hookup to insert into templates.

can.view.hook(callback)

Registers a hookup function that can be called back after the html is put on the page. Typically this is handled by the template engine.

var id = can.view.hook(function(el){
    //do something with el
}),
html = "<div data-view-id='"+id+"'>"
$('.foo').html(html);

What is the purpose of the can.view.live object?

Setup live-binding between the DOM and a compute manually. An object with the live-binding methods: can.view.live.html, can.view.live.list, can.view.live.text, can.view.live.attr and can.view.live.attrs.

The can.view.live object is an object with utlitiy methods for setting up live-binding in relation to different parts of the DOM and DOM elements. For example, to make an <h2>'s text stay live with a compute:

var text = can.compute("Hello World");
var textNode = $("h2").text(" ")[0].childNodes[0];
can.view.live.text(textNode, text);

What is the purpose of the can.view.live.attr function?

Keep an attribute live to a can.compute.

live.attr(el, attributeName, compute)
  1. el {HTMLElement}: The element whos attribute will be kept live.
  2. attributeName {String}: The attribute name.
  3. compute {can.compute(getterSetter, context)}: The compute
var div = document.createElement('div');
var compute = can.compute("foo bar");
can.view.live.attr(div,"class", compute);

What is the purpose of the can.view.live.html method?

Live binds a compute's value to a collection of elements.

live.html(el, compute, parentNode, nodeList)
  1. el {HTMLElement}: An html element to replace with the live-section.
  2. compute {can.compute(getterSetter, context)}: A can.compute whose value is HTML.
  3. parentNode {HTMLElement}: An overwritable parentNode if el's parent is a documentFragment.

can.view.live.html is used to setup incremental live-binding.

// a compute that change's it's list
var greeting = can.compute(function(){
    return "Welcome <i>"+me.attr("name")+"</i>"
});
var placeholder = document.createTextNode(" ");
$("#greeting").append(placeholder);
can.view.live.html( placeholder,  greeting );

What is the purpose of the can.view.live.replace function?

Replaces one element with some content while keeping can.view.live.nodeLists data correct.

live.replace(nodes, val, teardown)
  1. nodes {Array<HTMLElement>}: An array of elements. There should typically be one element.
  2. val {String | HTMLElement | DocumentFragment}: The content that should replace nodes. If a string is passed, it will be hookedup.
  3. teardown {function()}: A callback if these elements are torn down.

What is the purpose of the can.view.live.text function?

Replaces one element with some content while keeping can.view.live.nodeLists data correct.

live.text(el, compute, parentNode, nodeList)

What is the purpose of the can.view.register function?

Registers a template engine to be used with view helpers and compression.

can.view.register(info)
  1. info {Object}: Information about the templating language.
    1. plugin {String}: The location of the templating language's plugin.
    2. suffix {String}: Files with this suffix will use this templating language's plugin by default.
    3. renderer {function()}: A function that returns a function that, given data, will render the template with that data. The renderer function receives the id of the template and the text of the template.
    4. script {function()}: A function that returns the string form of the processed template.
can.view.register({
    suffix : "tmpl",
    plugin : "jquery/view/tmpl",
    renderer: function( id, text ) {
        return function(data){
            return jQuery.render( text, data );
        }
    },
    script: function( id, text ) {
        var tmpl = can.tmpl(text).toString();
        return "function(data){return ("+
            tmpl+
        ").call(jQuery, jQuery, data); }";
    }
})

What is the purpose of the can.autorender method?

Registers functions to callback when all templates successfully render or an error in rendering happens.

can.autorender(succcess, error)
  1. success {function()}: A function to callback when all autorendered templates have been rendered successfully.
  2. error {function()}: A function to callback if a template was not rendered successfully.

As long is this module is part of your CanJS build or imported with RequireJS, StealJS, or SystemJS, can-autorender will automatically look for can-autorender tags and render them. Once all templates have finished rendering, it will call any callbacks passed to can.autorender().

<body>
    <script type='text/stache' can-autorender id='main' message="Hello World">
    <my-component>
        {{message}}
    </my-component>
    </script>

    <script src='jquery.js'></script>
    <!-- A CanJS build that includes this plugin -->
    <script src='can.custom.js'></script>
    <!-- All your app's code and components -->
    <script src='app.js'></script>
    <script>
        // Wait until everything has rendered.
        can.autorender(function(){
            // Update the viewModel the template was rendred with:
            $("#main").viewModel().attr("message","Rendered!");
        })
    </script>
</body>
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License