Статьи

Создайте менеджер контактов с помощью Backbone.js: часть 1

В этом руководстве мы рассмотрим создание полнофункционального менеджера контактов с использованием Backbone.js, Underscore.js и jQuery. Мы рассмотрим основные компоненты, которые делают Backbone, а также некоторые удобные методы, предоставляемые Underscore.


Backbone — это архитектурная структура, которая позволяет нам легко создавать нетривиальные приложения JavaScript, используя организацию и структуру в стиле MVC. Магистраль не считается истинной MVC — C для Collection, а не Controller, но она по-прежнему предлагает многие из тех же преимуществ и позволяет нам писать мощный, но обслуживаемый код.

Underscore — это служебная библиотека, которая предоставляет расширенные функциональные возможности JavaScript, добавляя дополнительные функции для работы с массивами, коллекциями, функциями и объектами.

Я уверен, что jQuery не нуждается в представлении здесь.


Нам понадобится корневая папка проекта, содержащая подпапки css , img и js , поэтому создайте их сейчас. Мы начнем со следующей HTML-страницы:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
<!DOCTYPE html>
 
<html lang=»en»>
    <head>
        <meta charset=»UTF-8″ />
        <title>Backbone.js Web App</title>
        <link rel=»stylesheet» href=»css/screen.css» />
    </head>
    <body>
        <div id=»contacts»></div>
        <script src=»js/jquery-1.7.1.min.js»></script>
        <script src=»js/Underscore-min.js»></script>
        <script src=»js/Backbone-min.js»></script>
        <script src=»js/app.js»></script>
    </body>
</html>

Сохраните его как index.html в корневой папке проекта. Единственное обязательное требование Backbone — Underscore.js, но мы также хотим использовать jQuery, поэтому перед Backbone мы будем ссылаться на эти две библиотеки. Код нашего приложения войдет в app.js а любые стили — в screen.css . На странице у нас есть пустой контейнер, который станет основой нашего приложения.

Затем мы можем создать скелетный файл JavaScript, который мы будем постепенно заполнять в течение этой серии. В новом файле добавьте следующий код:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
(function ($) {
 
    var contacts = [
        { name: «Contact 1», address: «1, a street, a town, a city, AB12 3CD», tel: «0123456789», email: «[email protected]», type: «family» },
        { name: «Contact 2», address: «1, a street, a town, a city, AB12 3CD», tel: «0123456789», email: «[email protected]», type: «family» },
        { name: «Contact 3», address: «1, a street, a town, a city, AB12 3CD», tel: «0123456789», email: «[email protected]», type: «friend» },
        { name: «Contact 4», address: «1, a street, a town, a city, AB12 3CD», tel: «0123456789», email: «[email protected]», type: «colleague» },
        { name: «Contact 5», address: «1, a street, a town, a city, AB12 3CD», tel: «0123456789», email: «[email protected]», type: «family» },
        { name: «Contact 6», address: «1, a street, a town, a city, AB12 3CD», tel: «0123456789», email: «[email protected]», type: «colleague» },
        { name: «Contact 7», address: «1, a street, a town, a city, AB12 3CD», tel: «0123456789», email: «[email protected]», type: «friend» },
        { name: «Contact 8», address: «1, a street, a town, a city, AB12 3CD», tel: «0123456789», email: «[email protected]», type: «family» }
    ];
 
} (jQuery));

Сохраните это в папке js как app.js Мы поместим весь наш код в анонимную функцию, которую мы вызываем немедленно, добавив псевдоним jQuery в качестве символа $ . На этом этапе также определен массив объектов, каждый из которых представляет контакт.

Мы будем использовать это локальное хранилище данных для этой части руководства, поскольку оно позволяет нам запускать некоторые скрипты и не беспокоиться о синхронизации с сервером. Мы сохраним это на другой день!


Модель представляет данные приложения; в нашем приложении это будет отдельный контакт, который будет иметь такие атрибуты, как имя, номер контакта и т. д. Можно сказать, что отдельная модель представляет атомарную часть приложения — наименьшую возможную единицу функциональности. Добавьте следующий код непосредственно после массива данных:

1
2
3
4
5
var Contact = Backbone.Model.extend({
    defaults: {
        photo: «/img/placeholder.png»
    }
});

Чтобы создать модель в Backbone, мы просто расширяем класс Backbone.Model используя метод extend() . Мы можем передать объект в метод, который позволяет нам настраивать модель с нашей собственной функциональностью. Одно из свойств, которые мы можем установить в этом объекте, называется defaults . Это свойство позволяет нам настраивать значения по умолчанию для любого атрибута, который мы хотели бы иметь в наших моделях.

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

Модели имеют другие свойства, которые мы можем использовать для добавления функциональности; мы могли бы определить метод initialize() , и Backbone автоматически вызовет этот метод для нас при инициализации каждой модели. Мы не будем использовать это в настоящее время, но не волнуйтесь, мы вернемся к моделям чуть позже.


Коллекция — это класс для управления группами моделей. Мы будем использовать простую в этом примере, чтобы сохранить все наши контакты. Добавьте следующий код сразу после модели Contact :

1
2
3
var Directory = Backbone.Collection.extend({
    model: Contact
});

Как и модель, коллекция — это класс Backbone, который мы расширяем для добавления пользовательских функций, специфичных для нашего приложения. Коллекции также имеют метод extend() , и он принимает объект, который позволяет нам устанавливать свойства класса и добавлять поведение. Мы используем свойство model чтобы сообщить коллекции, из какого класса должен быть построен каждый элемент коллекции, что в данном случае является экземпляром нашей модели Contact . Не беспокойтесь, что классы, которые мы определили до сих пор, кажутся чрезвычайно простыми, мы вернемся и добавим дополнительные функциональные возможности в последующих частях руководства.


Представления отвечают за отображение данных приложения на HTML-странице. Одним из преимуществ разделения частей приложения, которые обрабатывают данные, и частей, которые отображают данные, является то, что мы можем очень легко внести изменения в один, не требуя значительных изменений в другом. Мы будем использовать пару представлений в нашем приложении, первое из которых должно быть добавлено непосредственно после класса Directory :

01
02
03
04
05
06
07
08
09
10
11
12
var ContactView = Backbone.View.extend({
    tagName: «article»,
    className: «contact-container»,
    template: $(«#contactTemplate»).html(),
 
    render: function () {
        var tmpl = _.template(this.template);
 
        this.$el.html(tmpl(this.model.toJSON()));
        return this;
    }
});

Этот вид обрабатывает отображение отдельного контакта. Подобно моделям и коллекциям, представления имеют метод extend() используемый для расширения класса Backbone.View . Мы устанавливаем несколько свойств экземпляра в нашем представлении; свойство tagName используется для указания контейнера для представления, а свойства className указывают имя класса, которое добавляется в этот контейнер. Мы будем использовать простой шаблон в нашем HTML-файле для визуализации каждого контакта, поэтому свойство template хранит кэшированную ссылку на шаблон, который мы выбираем на странице с помощью jQuery.

Далее мы определяем функцию render() ; эта функция не вызывается автоматически Backbone, и хотя мы можем вызывать ее из автоматически вызванного метода initialize() чтобы сделать представление самопредставленным, нам в этом нет необходимости.

В методе render() мы храним ссылку на метод template() Underscore и передаем ему сохраненный шаблон. Когда передается единственный аргумент, содержащий шаблон, Underscore не вызывает его сразу, а возвращает метод, который можно вызвать для фактической визуализации шаблона.

Затем для удобства мы устанавливаем HTML-содержимое элемента <article> созданного представлением, в интерполированный шаблон, используя метод html() jQuery. Это делается путем вызова шаблонной функции, которую Underscore возвратил ранее, и передачи ей данных для интерполяции. Данные получены из модели с использованием метода toJSON() Backbone на модели. Интерполяция означает, что токены в шаблоне заменяются фактическими данными. Также обратите внимание, что мы используем $el для установки содержимого HTML; это кэшированный объект jQuery, представляющий текущий элемент, поэтому нам не нужно продолжать создавать новые объекты jQuery.

В конце метода render() мы возвращаем объект this , который указывает на экземпляр render() вызывается метод render() . Это сделано для того, чтобы мы могли связать другие методы Backbone с экземпляром представления после вызова его метода render() .


Вероятно, сейчас самое время взглянуть на встроенные возможности Underscore для микро-шаблонов. Underscore предоставляет метод template() как мы видели, для использования и интерполяции шаблонов. К HTML-странице мы должны добавить шаблон, который будем использовать; добавьте следующий код непосредственно после контейнера контактов <div> :

1
2
3
4
5
6
7
8
9
<script id=»contactTemplate» type=»text/template»>
    <img src=»<%= photo %>» alt=»<%= name %>» />
    <h1><%= name %><span><%= type %>
    <div><%= address %></div>
    <dl>
        <dt>Tel:</dt><dd><%= tel %></dd>
        <dt>Email:</dt><dd><a href=»mailto:<%= email %>»><%= email %></a></dd>
    </dl>
</script>

Мы используем элемент <script> с атрибутом id чтобы мы могли легко его выбрать, и атрибут нестандартного type чтобы браузер не пытался его выполнить. В шаблоне мы указываем структуру HTML, которую мы хотели бы использовать, и используем токены, чтобы указать, куда следует вставить данные модели. Есть несколько других функций, которые мы можем использовать с Underscore, включая интерполяцию значений, экранированных HTML, или выполнение произвольного JavaScript, но нам не нужно использовать их для целей данного руководства.


Чтобы завершить эту часть урока, мы собираемся создать еще один вид. Наше текущее представление представляет каждый отдельный контакт, поэтому отображается на модель в соотношении 1: 1. Но эта точка зрения не является само-рендерингом, и мы еще не вызывали ее. Нам нужен вид, который отображает 1: 1 в нашу коллекцию, основной вид, который будет отображать правильное количество видов контактов для отображения каждого из наших контактов. Сразу после ContactView добавьте следующий класс:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
var DirectoryView = Backbone.View.extend({
    el: $(«#contacts»),
 
    initialize: function () {
        this.collection = new Directory(contacts);
        this.render();
    },
 
    render: function () {
        var that = this;
        _.each(this.collection.models, function (item) {
            that.renderContact(item);
        }, this);
    },
 
    renderContact: function (item) {
        var contactView = new ContactView({
            model: item
        });
        this.$el.append(contactView.render().el);
    }
});

Это представление будет прикреплено к элементу, который уже существует на странице, к пустому контейнеру, жестко запрограммированному в <body> , поэтому мы выбираем элемент с помощью jQuery и устанавливаем его в качестве свойства el . Затем определите простую функцию initialize() которая создает экземпляр нашего класса коллекции, а затем вызывает собственный метод render() , что делает само представление визуализируемым.

Затем мы определяем метод render() для нашего основного представления. Внутри функции мы храним ссылку на представление, чтобы мы могли получить к нему доступ в функции обратного вызова, а затем используем метод each() Underscore для итерации по каждой модели в нашей коллекции.

Этот метод принимает два аргумента (в этой форме, хотя его также можно использовать только с одним аргументом); первая — это набор элементов для итерации, вторая — анонимная функция, выполняемая для каждого элемента. Эта функция обратного вызова принимает текущий элемент в качестве аргумента. Все, что мы делаем в этой функции обратного вызова, — это renderContact() метод renderContact() и передаем ему текущий элемент.

Наконец, мы определяем метод renderContact() . В этом методе мы создаем новый экземпляр нашего класса ContactView (помните, класс ContactView представляет отдельный контакт) и устанавливаем его свойство model для элемента, переданного в метод. Затем мы добавляем элемент, созданный путем вызова метода render() к свойству $el основного представления DirectoryView (пустой контейнер, который мы выбрали на странице). Свойство $el является кэшированным объектом jQuery, который Backbone создает для нас автоматически.

Основной вид отвечает за создание каждой отдельной модели в нашей коллекции. Все, что нам нужно сделать, это инициализировать наше главное представление, и, поскольку оно само-рендеринг, оно отобразит все контакты, указанные в массиве фиктивных данных:

1
var directory = new DirectoryView();

Когда мы запустим эту страницу в браузере сейчас, мы должны увидеть визуальное представление наших данных:

Backbone создает экземпляр модели для каждого элемента в нашем исходном массиве, который хранится в нашей коллекции и отображается как экземпляр представления.

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


В этой части руководства мы познакомились с некоторыми основными компонентами Backbone.js; модели, коллекции и виды. Модели — это классы, которые мы можем создать для хранения данных о конкретной вещи и определения поведения для нее. Коллекции используются для управления группами моделей, а представления позволяют нам отображать наши модели, используя интерполированные шаблоны, которые отображают данные из наших моделей.

В следующей части урока мы рассмотрим, как мы можем отфильтровать нашу коллекцию, чтобы отобразить только подмножество наших моделей. Мы также рассмотрим еще один важный компонент Backbone — маршрутизаторы.