Select the correct library
It is very difficult to create a JS APP without good tools. jQuery is just a library for operating DOM and does not provide any basis for creating an APP. This is why we want a special library similar to CanJS.
CanJS is a lightweight MVC library that provides the tools you need to create a JS APP.
CanJS is a lightweight MVC library that provides the tools you need to create a JS APP. It provides a basic framework with MVC (Model-View-Control) mode, dynamic template binding, route support and memory security. It also supports jQuery, Zepto, Mootools, YUI, Dojo, and has rich extensions and plug-ins.
Part One You'll Learn:
Create Control and View Layers (UI Templates) to display contacts
Use Model Model Layer to represent data
Use fixtures plugin to simulate ajax to return data
You must be excited! Let's start coding.
Create your folders and HTML
You first create a folder for your APP, and then create 4 subfolders in the directory: css, js, views and img. as follows:
contacts_manager
css
js
views
img
Save the following code as index.html:
The code copy is as follows:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>CanJS Contacts Manager</title>
<link rel="stylesheet" href="css/bootstrap.min.css">
<link rel="stylesheet" href="css/contacts.css">
</head>
<body>
<div>
<div>
<div>
<h1>Contacts Manager</h1>
</div>
</div>
<div>
<div>
<div>
<nav id="filter"></nav>
</div>
</div>
<div>
<div id="create"></div>
<div id="contacts"></div>
</div>
</div>
</div>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.js"></script>
<script src="js/can.jquery.min.js"></script>
<script src="js/can.fixture.js"></script>
<script src="js/contacts.js"></script>
</body>
</html>
At the bottom of the page you load the required JS (including your app: contacts.js).
The CSS and image files used in the tutorial can be downloaded.
Use View to create your UI
View is used to render the UI template of your app. CanJS supports a variety of template engines. This article uses EJS, which includes and supports dynamic binding.
The tags of EJS templates are very similar to HTML and support containing JS code. The three commonly used tags are as follows:
<% CODE %> Execute JS
<%= CODE %> Execute JS and write non-escaped results to HTML at the current location
<%== CODE %> Execute JS and write the escaped result to HTML at the current location (for sub-templates).
The template can be loaded from a file or script tag, and this tutorial is loaded from an EJS file.
Show contacts
To create a contact, you have to create an EJS template first and save the following code as contactsList.ejs into your views folder:
The code copy is as follows:
<ul>
<% list(contacts, function(contact){ %>
<li <%= (el)-> el.data('contact', contact) %>>
<%== can.view.render('views/contactView.ejs', {
contact: contact, categories: categories
}) %>
</li>
<% }) %>
</ul>
contactLists.ejs will render a contact list. Let's analyze this template:
The code copy is as follows:
<% list(contacts, function(contact){ %>
If the callback method in the list() method is used in conjunction with the list with the observer, it will be called repeatedly using dynamic binding once the data of the list changes.
The code copy is as follows:
<li <%= (el)-> el.data('contact', contact) %>>
The above code generates a <li> with contact data through the element's callback method. After the method after the arrow is executed, the data of the el object is set to the corresponding element.
The code copy is as follows:
<%== can.view.render('views/contactView.ejs', {
contact: contact, categories: categories
}) %>
The above code renders the sub-template contactView.ejs into a contact person. can.view.render() returns HTML with template and data as parameters.
Render a single contact
Sub-templates are a good way to organize views into manageable blocks. It also makes your templates simple and easy to reuse. This template will be used later in the tutorial to create a contact, save the following code as contactView.ejs and enter the views folder:
The code copy is as follows:
<a href="javascript://"><i></i></a>
<form>
<div>
<div>
<img src="img/contact.png">
</div>
<div>
<input type="text" name="name" placeholder="Add Name"
<%= contact.attr('name') ? "value='" + contact.name + "'" : "class='empty'" %>>
<select name="category">
<% $.each(categories, function(i, category){ %>
<option value="<%= category.data %>" <%= contact.category === category.data ? "selected" : "" %>>
<%= category.name %>
</option>
<% }) %>
</select>
</div>
<div>
<label>Address</label>
<input type="text" name="address"
<%= contact.attr('address') ? "value='" + contact.address + "'" : "class='empty'" %>>
<label>Phone</label>
<input type="text" name="phone"
<%= contact.attr('phone') ? "value='" + contact.phone + "'" : "class='empty'" %>>
<label>Email</label>
<input type="text" name="email"
<%= contact.attr('email') ? "value='" + contact.email + "'" : "class='empty'" %>>
</div>
</div>
</form>
The contact attributes are placed in the <input> tag, which allows you to edit and update the user's information.
Revitalize your View (Good Literature..)
If attr() is used during the EJS processing template, the code around it will be handed over to the event processor to monitor the changes in the corresponding attributes. When the attributes change, the associated UI in the APP will be updated. This function benefits from the template dynamic binding mechanism. EJS dynamic binding is selective and will only be enabled for the corresponding attributes when attr() is used.
We use a <input> tag in contactView.ejs to understand its usage:
The code copy is as follows:
<input type="text" name="name" placeholder="Add Name"
<%= contact.attr('name') ? "value='" + contact.name + "'" : "class='empty'" %>>
The code in the special tag will be converted into an event and bound to the name attribute of this contact. When the name attribute changes, the event will be triggered and the HTML structure will be updated.
Use can.Control to handle business logic
can.Control creates an organized, internally leak-free, full control that can be used to create widgets or handle business logic. You create a Control instance for a DOM element through the required data, and you can define method binding events in your Control.
When the element associated with Control is deleted from the DOM, Control will destroy itself and clear the bound method.
To create a Control, inherit by passing in the object you defined containing the function to can.Control(). The next incident was also reported.
Each Control instance has several important values and method specifications:
References to this Control instance
The DOM element you created in this.element instance
this.options Parameter objects required to create an instance
init() is called when the instance is created successfully
Manage contacts
Add the following code snippet to the contacts.js file to create a control for managing contacts:
The code copy is as follows:
Contacts = can.Control({
init: function(){
this.element.html(can.view('views/contactsList.ejs', {
contacts: this.options.contacts,
categories: this.options.categories
}));
}
})
When an instance of Contacts is created, init() does two things:
Use can.view() to render contacts. can.view() receives two parameters: a file containing templates and data or a strip tag; it returns a documentFragment (a lightweight container that manages DOM elements).
Use jQuery.html() to insert the documentFragment of can.view() into the element of the Control
Use Model to represent data
Model is the abstract layer of APP data. This app uses two models: one corresponding to the contact person and the other corresponding to the category. Add the following code to contacts.js:
The code copy is as follows:
Contact = can.Model({
findAll: 'GET /contacts',
create : "POST /contacts",
update : "PUT /contacts/{id}",
destroy : "DELETE /contacts/{id}"
},{});
Category = can.Model({
findAll: 'GET /categories'
},{});
A model has 5 methods that may define CRUD data, namely findAll, findOne, create, update and destroy. You can rewrite these methods, but the best way is to use the REST service (Representational State Transfer). Just like the above code, you can worry about ignoring the static methods that will not be used in the APP.
It is important to point out here that the model instance is actually 'observables' from CanJS. can.Observe provides the observer pattern of an object can.Observe.List provides the observation pattern of an array. This means that you can get and set data through attr() while listening for changes in data.
The findAll() method returns a Model.list, which is the event triggered by can.Observe.List when the element is added or removed.
Use Fixture to imitate Rest
Fixture intercepts AJAX requests and simulates the response through files or methods. This is very useful for testing, or when the backend is not ready. Fixture is what the APP model simulates REST.
First, you need to prepare some data to fixture and add the following code to:
The code copy is as follows:
var CONTACTS = [
{
id: 1,
name: 'William',
address: '1 CanJS Way',
email: '[email protected]',
phone: '0123456789',
category: 'co-workers'
},
{
id: 2,
name: 'Laura',
address: '1 CanJS Way',
email: '[email protected]',
phone: '0123456789',
category: 'friends'
},
{
id: 3,
name: 'Lee',
address: '1 CanJS Way',
email: '[email protected]',
phone: '0123456789',
category: 'family'
}
];
var CATEGORIES = [
{
id: 1,
name: 'Family',
data: 'family'
},
{
id: 2,
name: 'Friends',
data: 'friends'
},
{
id: 3,
name: 'Co-workers',
data: 'co-workers'
}
];
With the data, connect it to fixture to simulate REST. can.fixture() receives two parameters. The URL we want to intercept and the files and methods we respond to. Usually the URLs you want to intercept are dynamic and follow a pattern. Just add wildcards enclosed with {} in the URL.
Add the following code to contacts.js:
The code copy is as follows:
can.fixture('GET /contacts', function(){
return [CONTACTS];
});
var id= 4;
can.fixture("POST /contacts", function(){
return {id: (id++)}
});
can.fixture("PUT /contacts/{id}", function(){
return {};
});
can.fixture("DELETE /contacts/{id}", function(){
return {};
});
can.fixture('GET /categories', function(){
return [CATEGORIES];
});
The first four fixtures simulate the GET, POST, PUT and DELETE responses of the Contact model, and the fifth simulates the GET response of the Category model.
Start the APP
Your app has a model that manages data, renders contacts' views, and organizes all of this into Control. All you have to do now is to start the APP. Now you need to kickstart the application!
Add the following code to contacts.js:
The code copy is as follows:
$(document).ready(function(){
$.when(Category.findAll(), Contact.findAll()).then(
function(categoryResponse, contactResponse){
var categories = categoryResponse[0],
contacts = contactResponse[0];
new Contacts('#contacts', {
contacts: contacts,
categories: categories:
});
});
});
Let's analyze this code:
The code copy is as follows:
$(document).ready(function(){
Use the jQuery.ready method to listen for the ready of the DOM.
The code copy is as follows:
$.when(Category.findAll(), Contact.findAll()).then(
function(categoryResponse, contactResponse){
Call the findAll() method of two models to get the types of all contacts. Since findAll() has a delay, $.when() ensures that the callback method is executed only after the two requests are completed at the same time.
The code copy is as follows:
var categories = categoryResponse[0],
contacts = contactResponse[0];
Get the dataset of the corresponding Model instance from the two findAll() methods. is the first element of the array returned by the response.
The code copy is as follows:
new Contacts('#contacts', {
contacts: contacts,
categories: categories:
});
Creates a Control of the Contact for the #contacts element. Contact and type datasets are sent to Control.
Open your APP with a browser and you will see the following contact list:
Summarize
This is the first chapter in the tutorial series, and you have already learned about the core of CanJS:
Models Abstraction layer of your APP data
Views templates that convert data into HTML
Controls Organizations relate to everything