Backbone.js: Views. Часть первая.
Сегодня затронем наиболее обширную тему: отображения(views). Думаю, что будет две части, так как тема всё-таки достаточно большая.
Итак, начнём. Для начала разберёмся с терминологией. В backbone.js отображения (views) — это совсем не то же самое, что в «рельсах». Если Вы помните, то в «рельсах» views представляют из себя просто шаблоны, в которые вставляются некоторые данные. Иногда в них присутствует некоторая простейшая логика, позволяющая отображать список однотипных элементов и т.п. В backbone.js же views больше похожи на контроллеры в «рельсах»: они оперируют данными и ответственные за то, какие данные в каком шаблоне будут отображаться. Шаблоны тоже присутствуют, но они никак отдельно не выделены.
Views будем изучать на примере пользователей — users. И начнём, как бы странно это не казалось, с роутеров. Почему? Да потому что там мы впервые сталкиваемся с вызовом классов views. Но и это не самое начало. Если помните, то в части про роутеры, я рассказывал ,что в момент инициализации нашего приложения, создаются две коллекции: для постов и для пользователей. На данный момент нас будет интересовать коллекция пользователей. Вот строчки из конструктора App.Routers.Posts:
@users.reset($('#all_users_data').data('users'))
В первой строке мы создаём экземпляр класса коллекции для пользователей, а во второй вызываем метод reset(), который загружает данные в нашу коллекцию. Об этом я уже говорил раньше. Таким образом, на момент полной загрузки странички с нашим приложением, у нас есть вся необходимая информация для работы с пользователями.
Теперь представим, что пользователь кликает по ссылке или в адресной строке набирает путь «/#users«. В соответствии с нашими роутерами, управление передаётся функции users() из всё того же класса App.Routers.Posts:
$('.current').removeClass('current')
$('#all_users').addClass('current')
view = new App.Views.UsersIndex(collection: @users)
@showView('#content', view)
Первые две строчки не представляют интереса — это всего лишь украшательства. А вот последние две строчки как раз и отвечают за то, чтобы мы увидели правильную информацию. В 3-ей строке мы создаём экземпляр класса App.Views.UsersIndex, который и будет отвечать за отображение пользователей в приложении. Настоятельно обращаю Ваше внимание, что ему в качестве аргумента передаётся наша коллекция пользователей (@users), которую, собственно, нам и предстоит отобразить. Рассмотрим теперь, собственно сам класс App.Views.UsersIndex:
template: JST['users/index']
initialize: ->
@collection.on('reset', @render, @)
render: ->
$(@el).html(@template())
@collection.each(@appendUser)
@
appendUser: (user) =>
view = new App.Views.UserItem(model: user, true)
@$('#userslist').prepend(view.render().el)
Как видим, данный класс наследуется от Backbone.View — это и не удивительно, т.к. мы и хотим создать отображение. (-: В следующей строке задаётся функция, которая указывает, где и какой шаблон использовать для построения html-разметки. В данном случае мы будем использовать Javascript Template(JST). Но и это ещё не всё, что касается шаблонизатора. Лично я буду использовать Eco Template Engine, который позволяет легко и просто делать шаблоны с применением синтаксиса, похожего на erb. Если Вы посмотрите на файлы в директории /templates, то там все файлы с расширением *.jst.eco — это невероятно удобно (-: Кстати, вот содержимое самого файла /templates/users/index.jst.eco:
User
</div>
<div style="margin-bottom: 20px;">
Action
</div>
<div class='usersList'>
<ul id="userslist"></ul>
</div>
<div class="form-actions">
<a href="#users/new" class="btn btn-mini">Add User</a>
</div>
В данном файле просто задётся разметка, для дальнейшего отображения пользователей.
Но вернёмся к нашему классу отображения. Дальше следует конструктор нашего класса:
@collection.on('reset', @render, @)
Тут у нас всего одна единственная строчка, которая выполняет очень важную функцию: привязывает функцию-обработчик @render() к событию reset для коллекции @collection, которая будет вызываться каждый раз, когда сработает событие reset. В переводе на русский язык, когда в нашу коллекцию будут загружены данные(reset), их необходимо будет отобразить(@render()). @collection — это та коллекция, которая нам была передана в момент создания экземпляра данного класса, в нашем случае это users.
Рассмотрим теперь саму функцию render().
$(@el).html(@template())
@collection.each(@appendUser)
@
Набор каких-то мало понятных символов, не так ли? (-: На самом деле всё достаточно просто. Но обо всём по-порядку.
el — это DOM-элемент нашего документа, куда будет вставлено наше отображение.
@el указывает на то, что это внешняя переменная, что она нам была передана на момент вызова нашей функции.
$(@el) — это просто jQuery способ выбрать соответствующий DOM-элемент в разметке страницы.
А теперь вся эта строка:
$(@el).html(@template()) — заменить содержимое элемента el нашим шаблоном. Только и всего.
Следующая строка не что иное, как перебор всех экземпляров в коллекции и для каждого вызов функции appendUser().
Функция appendUser() выполняет абсолютно туже функцию, только касательно не коллекции, а отдельной записи — модели. Попробую описать, что это и для чего. Я сделал так, что каркас для коллекции строится сам по себе, а уже в этот каркас добавляются данные для каждой модели отдельно. В принципе, можно было сделать так, что уже в шаблоне пробегать по все коллекции и отрисовывать каждую модель. Но такой способ ведёт к неприятным последствиям. Представьте, например, что Вам на странице надо отобразить сотню или тысячу элементов. Не вопрос, отобразить не проблема: ну подумаешь отрисовываются все элементы, это не проблема. Проблемы начинаются, когда над эти данными начинают производить какие-то операции: добавление, удаление и т.п. Так вот при всех этих процессах придётся каждый раз прорисовывать все сто или тысячу элементов! А это уже не оправданная нагрузка. Чтобы этого не происходило, за отрисовку каждого элемента отвечает свой собственный класс, всё просто и красиво (-:
Мне остаётся только добавить, что в функции appendUser() я использую jQuery функцию prepend(), чтобы в результате недавно добавленные пользователи были выше в списке, чем давно добавленные.
Ну и последняя строка в фукции render() — это просто песня: символ @. (-: Это просто Coffeescript аналог, синоним слова this из Javascript-а. Просто приведу цитату из документации backbone.js, для чего это необходимо:
Хорошее соглашение — делать return this в конце render, чтобы иметь возможность делать цепочечные вызовы.
Так как Coffeescript намного упрощает написание кода, то нам просто достаточно написать «@», что и означает return this. Красота! (-:
Вот, в принципе, и всё для первой части про views. В следующий раз поговорим про некоторые другие аспекты отображений: добавление, удаление и редактирование пользователей, а так же клиентская и серверная валидация.
Недавние записи
- Long time no see
- Ошибка при обновлении Meteor до версии 1.4.2
- Patch falcon для ruby-1.9.3-p327
- Ускоряем ruby и Rails
- Gem ‘seo_params’: определение основных параметров
- Отрицательный margin-top и Opera
- Rspec and Devise reset password
- Backbone.js: Views. Часть первая.
- Backbone.js: Models and Collections
- Backbone.js Routers