Статьи

Погружение в CanJS

Если вы еще не слышали, в городе появился новый ребенок: CanJS . Какой лучший способ погрузиться в то, что предлагает этот новый фреймворк, чем создание приложения диспетчера контактов? Когда вы закончите эту серию из трех частей, у вас будут все инструменты, необходимые для создания ваших собственных приложений JavaScript!


Создать приложение JavaScript без нужных инструментов сложно. Хотя jQuery отлично справляется со своей задачей, библиотека манипулирования DOM не предоставляет никакой инфраструктуры для создания приложений. Именно поэтому вам нужно использовать библиотеку, например, CanJS.

CanJS — это легкая библиотека MVC, которая предоставляет вам инструменты, необходимые для создания приложений JavaScript.

CanJS — это легкая библиотека MVC, которая предоставляет вам инструменты, необходимые для создания приложений JavaScript. Он предоставляет всю структуру шаблона MVC (Model-View-Control), шаблоны с динамическим связыванием, поддержкой маршрутизации и безопасен для памяти . Он поддерживает jQuery, Zepto, Mootools, YUI, Dojo и имеет богатый набор расширений и плагинов.

В первой части вы будете:

  • Создайте элемент управления и просмотра (шаблон на стороне клиента) для отображения контактов
  • Представлять данные с использованием моделей
  • Имитация ответов ajax с помощью плагина fixtures

В восторге? Вы должны быть! Теперь давайте получим кодирование.


Вам нужно будет создать папку для вашего приложения. Внутри этой папки вам нужны четыре подпапки: css , js , views и img . Ваша структура папок должна выглядеть следующим образом:

  • contacts_manager
    • CSS
    • JS
    • взгляды
    • IMG

Сохраните это как index.html :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<!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 class=»container»>
      <div class=»row»>
        <div class=»span12″>
          <h1>Contacts Manager</h1>
        </div>
      </div>
      <div class=»row»>
        <div class=»span3″>
          <div class=»well»>
            <nav id=»filter»></nav>
          </div>
        </div>
        <div class=»span9″>
          <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>

В нижней части страницы мы загружаем jQuery, CanJS, плагин фикстуры и код вашего приложения ( contacts.js ).

CSS и изображения для этого урока включены в исходные файлы, которые можно скачать выше.

Представления — это шаблоны на стороне клиента, которые используются для визуализации частей вашего приложения. CanJS поддерживает несколько языков шаблонов, но в этом руководстве будет использоваться EJS (Embedded JavaScript), который поставляется с CanJS и поддерживает динамическое связывание.

Шаблоны EJS выглядят как HTML, но с волшебными тегами, где вам нужно динамическое поведение (с использованием JavaScript). В EJS есть три типа магических меток:

  • запускает код JavaScript,
  • выполняет оператор JavaScript и записывает экранированный результат в результирующий HTML,
  • выполняет оператор JavaScript и записывает неэкранированный результат в результирующий HTML (используется для под-шаблонов).

Шаблоны могут быть загружены из файла или тега скрипта. В этом уроке шаблоны будут загружаться из файлов EJS.


Для рендеринга контактов вам понадобится шаблон EJS. Сохраните следующий код как contactsList.ejs в вашей папке представлений:

1
2
3
4
5
6
7
8
9
<ul class=»clearfix»>
  <% list(contacts, function(contact){ %>
    <li class=»contact span8″ <%= (el)-> el.data(‘contact’, contact) %>>
      <%== can.view.render(‘views/contactView.ejs’, {
        contact: contact, categories: categories
      }) %>
    </li>
  <% }) %>
</ul>

contactLists.ejs отобразит список контактов. Давайте рассмотрим код шаблона здесь более подробно:

1
<% list(contacts, function(contact){ %>

Помощник EJS list() вызывает функцию обратного вызова для каждого контакта в списке. При использовании с наблюдаемым списком помощник list() будет использовать динамическое связывание для повторного запуска в любое время, когда изменяется длина списка.

1
<li class=»contact span8″ <%= (el)-> el.data(‘contact’, contact) %>>

Приведенный выше код использует функцию обратного вызова элемента для добавления экземпляра контакта к данным <li> . Все, что находится после стрелки, обернуто в функцию, которая будет выполняться с el установленным в текущий элемент.

1
2
3
<%== can.view.render(‘views/contactView.ejs’, {
  contact: contact, categories: categories
}) %>

Этот код отображает под-шаблон contactView.ejs для каждого контакта. can.view.render() принимает шаблон и данные в качестве параметров и возвращает HTML.


Под-шаблоны — отличный способ организовать ваши представления в управляемые куски. Они также помогают упростить ваши шаблоны и продвигать DRY (не повторяйте себя). Позже в этом руководстве вы снова будете использовать этот шаблон для создания контактов. Сохраните этот код как contactView.ejs в папке представлений:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<a href=»javascript://» class=»remove»><i class=»icon-remove»></i></a>
<form>
<div class=»row»>
  <div class=»span2″>
    <img src=»img/contact.png» width=»100″ height=»100″>
  </div>
  <div class=»span3″>
    <input type=»text» name=»name» placeholder=»Add Name»
      <%= contact.attr(‘name’) ?
    <select name=»category»>
      <% $.each(categories, function(i, category){ %>
        <option value=»<%= category.data %>» <%= contact.category === category.data ?
          <%= category.name %>
        </option>
      <% }) %>
    </select>
  </div>
  <div class=»span3″>
    <label>Address</label>
    <input type=»text» name=»address»
      <%= contact.attr(‘address’) ?
    <label>Phone</label>
    <input type=»text» name=»phone»
      <%= contact.attr(‘phone’) ?
    <label>Email</label>
    <input type=»text» name=»email»
      <%= contact.attr(’email’) ?
  </div>
</div>
</form>

Каждое свойство контакта помещается в <input> . Они будут использоваться для добавления и обновления информации о контакте.


Каждый раз, когда EJS встречает attr() при обработке шаблона, он знает, что окружающий код должен быть превращен в обработчик событий, связанный с изменениями этого свойства. Когда свойство изменяется в другом месте приложения, запускается обработчик событий, и ваш пользовательский интерфейс будет обновлен. Это называется живым связыванием. EJS Live обязательна. Включается только если вы используете attr() для доступа к свойствам.

Давайте посмотрим на один из тегов <input> из contactView.ejs чтобы увидеть, как это работает

1
2
<input type=»text» name=»name» placeholder=»Add Name»
  <%= contact.attr(‘name’) ?

Код в магических тегах станет обработчиком событий, связанным со свойством имени контакта. Когда мы обновляем свойство name, запускается обработчик событий и HTML обновляется.


can.Control создает организованный элемент управления с сохранением памяти без утечек памяти, который можно использовать для создания виджетов или организации логики приложения. Вы создаете экземпляр элемента управления для элемента DOM и передаете ему данные, которые понадобятся вашему элементу управления. Вы можете определить любое количество функций в вашем элементе управления и привязать к событиям.

Когда элемент, к которому привязан ваш элемент управления, удаляется из DOM, он уничтожает себя, очищая все связанные обработчики событий.

Чтобы создать новый элемент управления, расширьте can.Control() , передав ему объект, содержащий функции, которые вы хотите определить. Во второй части также будут переданы обработчики событий.

В каждом экземпляре Control есть несколько важных переменных и функций:

  • this — ссылка на экземпляр Control
  • this.element — элемент DOM, на котором вы создали экземпляр
  • this.options — Объект, содержащий любые данные, переданные экземпляру при его создании
  • init() — Вызывается при создании экземпляра.

Добавьте следующий фрагмент в файл contacts.js чтобы создать элемент управления, который будет управлять контактами:

1
2
3
4
5
6
7
8
Contacts = can.Control({
  init: function(){
    this.element.html(can.view(‘views/contactsList.ejs’, {
      contacts: this.options.contacts,
      categories: this.options.categories
    }));
  }
})

Когда экземпляр Contacts создан, init() сделает две вещи:

  1. Использует can.view() для рендеринга контактов. can.view() принимает два параметра: файл или идентификатор тега script, содержащий код нашего шаблона и данные. Он возвращает результат в виде documentFragment (облегченный контейнер, который может содержать элементы DOM).
  2. Вставляет documentFragment из can.view() в элемент Control, используя jQuery’s .html() .

Модель абстрагирует уровень данных приложения. В этом приложении необходимы две модели: одна для контактов, другая для категорий. Добавьте этот код в contacts.js :

01
02
03
04
05
06
07
08
09
10
Contact = can.Model({
  findAll: ‘GET /contacts’,
  create : «POST /contacts»,
  update : «PUT /contacts/{id}»,
  destroy : «DELETE /contacts/{id}»
},{});
 
Category = can.Model({
  findAll: ‘GET /categories’
},{});

Модель имеет пять статических методов, которые вы можете определить для создания, извлечения, обновления и удаления данных. Они находят все, findOne , create , update и destroy . Вы можете перезаписать эти функции для работы с любым бэкэндом, но самый простой способ определить модель — использовать сервис REST, как показано в коде выше. Вы можете безопасно опустить любые статические методы, которые не будут использоваться в приложении.

Здесь важно отметить, что экземпляры модели в CanJS на самом деле являются тем, что мы называем «наблюдаемыми». can.Observe предоставляет наблюдаемый шаблон для объектов, а can.Observe.List предоставляет наблюдаемый шаблон для массивов. Это означает, что вы можете получать и устанавливать свойства с помощью attr() и связываться с изменениями в этих свойствах.

Метод findAll() возвращает Model.list , который представляет собой can.Observe.List который запускает события, когда элемент добавляется или удаляется из списка.


Светильники перехватывают запросы AJAX и имитируют их ответ с помощью файла или функции. Это отлично подходит для тестирования, создания прототипов или когда бэкэнд еще не готов. Приспособления необходимы для имитации службы REST, которую используют модели в этом приложении.

Но для начала вам понадобятся примеры данных для приборов. Добавьте следующий код в contacts.js :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
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’
  }
];

Теперь, когда у вас есть некоторые данные, вам нужно подключить их к приборам, чтобы вы могли смоделировать службу REST. can.fixture() принимает два параметра. Первый — это URL, который мы хотим перехватить, а второй — это файл или функция, которая используется для генерации ответа. Часто URL, которые вы хотите перехватить, являются динамическими и следуют шаблону. В этом случае вы должны использовать шаблонные URL. Просто добавьте фигурные скобки в URL, где вы хотите использовать подстановочные знаки.

Добавьте следующее в contacts.js :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
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];
});

Первые четыре прибора имитируют ответы GET , POST , PUT и DELETE для модели Contact , а пятый прибор имитирует отклик GET для модели Category .


В вашем приложении есть модели для ваших данных, представления для отображения контактов и элемент управления для подключения всего. Теперь вам нужно запустить приложение!

Добавьте это в ваш файл contacts.js :

01
02
03
04
05
06
07
08
09
10
11
12
$(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
      });
  });
});

Давайте внимательнее посмотрим на то, что происходит в этом коде:

1
$(document).ready(function(){

Подождите, пока DOM будет готов, используя функцию готовности документа jQuery.

1
2
$.when(Category.findAll(), Contact.findAll()).then(
  function(categoryResponse, contactResponse){

Вызовите findAll() для обеих моделей, чтобы получить все контакты и категории. Поскольку findAll() возвращает значение Deferred, $.when() используется для параллельного выполнения обоих запросов и выполнения обратного вызова после их завершения.

1
2
var categories = categoryResponse[0],
  contacts = contactResponse[0];

Получить список экземпляров модели по ответу на два вызова findAll() . Ответы — это массивы, первый индекс — список полученных экземпляров модели.

1
2
3
4
new Contacts(‘#contacts’, {
  contacts: contacts,
  categories: categories
});

Создайте экземпляр элемента управления #contacts элементе #contacts . Список контактов и категорий передается в Контроль.

Когда вы запускаете приложение в браузере, вы должны увидеть список контактов:


Это делает это для первой части этой серии! Вы познакомились с ядром CanJS:

  • Модели абстрагируют слой данных в вашем приложении
  • Представления — это шаблоны, которые превращают данные в HTML
  • Контролирует провод все.

На следующем уроке вы создадите Control и View для отображения категорий и будете использовать маршрутизацию для фильтрации контактов. Надеюсь увидеть тебя там!

Вопросов? Спроси внизу! Не могу дождаться, чтобы узнать больше? Вторая часть серии была размещена здесь !