Backbone.js: Views. Часть первая.

Сегодня затронем наиболее  обширную тему: отображения(views). Думаю, что будет две части, так как тема всё-таки достаточно большая.

Итак, начнём. Для начала разберёмся с терминологией. В backbone.js отображения (views) — это совсем не то же самое, что в «рельсах». Если Вы помните, то в «рельсах» views представляют из себя просто шаблоны, в которые вставляются некоторые данные. Иногда в них присутствует некоторая простейшая логика, позволяющая отображать список однотипных элементов и т.п. В backbone.js же views больше похожи на контроллеры в «рельсах»: они оперируют данными и ответственные за то, какие данные в каком шаблоне будут отображаться. Шаблоны тоже присутствуют, но они никак отдельно не выделены.

Views будем изучать на примере пользователей — users. И начнём, как бы странно это не казалось, с роутеров. Почему? Да потому что там мы впервые сталкиваемся с вызовом классов views. Но и это не самое начало. Если помните, то в части про роутеры, я рассказывал ,что в момент инициализации нашего приложения, создаются две коллекции: для постов и для пользователей. На данный момент нас будет интересовать коллекция пользователей. Вот строчки из конструктора App.Routers.Posts:

    @users = new App.Collections.Users()
    @users.reset($('#all_users_data').data('users'))

В первой строке мы создаём экземпляр класса коллекции для пользователей, а во второй вызываем метод reset(), который загружает данные в нашу коллекцию. Об этом я уже говорил раньше. Таким образом, на момент полной загрузки странички с нашим приложением, у нас есть вся необходимая информация для работы с пользователями.

Теперь представим, что пользователь кликает по ссылке или в адресной строке набирает путь «/#users«. В соответствии с нашими роутерами, управление передаётся функции users() из всё того же класса App.Routers.Posts:

  users: ->
    $('.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:

class App.Views.UsersIndex extends Backbone.View

  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:

<div style="width: 85%; float: left; margin-bottom: 20px;">
  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>

В данном файле просто задётся разметка, для дальнейшего отображения пользователей.

Но вернёмся к нашему классу  отображения. Дальше следует конструктор нашего класса:

  initialize: ->
    @collection.on('reset', @render, @)

Тут у нас всего одна единственная строчка, которая выполняет очень важную функцию: привязывает функцию-обработчик @render() к событию reset для коллекции @collection, которая будет вызываться каждый раз, когда сработает событие reset. В переводе на русский язык, когда в нашу коллекцию будут загружены данные(reset), их необходимо будет отобразить(@render()). @collection — это та коллекция, которая нам была передана в момент создания экземпляра данного класса, в нашем случае это users.

Рассмотрим теперь саму функцию render().

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. В следующий раз поговорим про некоторые другие аспекты отображений: добавление, удаление и редактирование пользователей, а так же клиентская и серверная валидация.

Недавние записи

Оставить комментарий