В предыдущем уроке мы погрузились в работу Backbone.js, инфраструктуры MV * JavaScript для создания приложений. Создавая небольшое приложение для серфинга (чтобы мы могли отслеживать доски для серфинга на складе), мы рассмотрели создание моделей, группирование экземпляров моделей в коллекции и перебор коллекций для создания представлений. Мы также сделали еще один шаг вперед, представив каждый из наших экземпляров модели в собственном представлении и сгруппировав их вместе в родительском представлении. Это где мы остановились .
В этом уроке мы увидим важность этого последнего шага и представим на рисунке некоторую логику контроллера. Я коснулся логики типа контроллера и ее места в прошлой статье, но давайте продолжим, прежде чем мы продолжим.
Разделение проблем
В среде MV * представления обычно отвечают за следующее:
- Визуализация пользовательского интерфейса и отображение данных, извлеченных из базы данных и / или коллекций для конечного пользователя.
- Обработка пользовательского ввода через события и как-то сообщать эти события модели.
В части «как-то» возникает логика контроллера. В Backbone.js мы можем привязывать события к представлению во время его создания. Если мы хотим добавить или удалить товар или полностью удалить модель, мы можем это сделать. Мы расскажем, как сделать это шаг за шагом, но сейчас просто представьте, что у нас есть кнопка в нашем представлении, которая позволила нам удалить один товар со склада. Мы можем зарегистрировать это событие в представлении, которое по сути является логикой контроллера. Когда эта кнопка нажата, мы связываемся с соответствующей моделью и вносим в нее необходимые обновления. Изменения в моделях также могут инициировать события, которые может обрабатывать представление, в конечном итоге повторно отображая представление и отображая правильные данные.
Итак, в двух словах:
- Представление отображает данные модели и регистрирует события.
- Когда событие запускается через пользовательский ввод, мы можем запустить какой-то обратный вызов или функцию, которая связывается с моделью.
- Внутри модели логика выполняется. В реальном сценарии вы, вероятно, также будете обновлять базу данных здесь.
- Изменение модели запускает событие.
- Представление обнаруживает это событие и действует соответствующим образом, возможно, перерисовывая себя.
Имея это в виду, теперь нам нужно подумать о том, как мы собираемся регистрировать эти события в первую очередь. Давайте перейдем к этому.
Использование магистральных событий
Согласно документации Backbone events :
События — это модуль, который можно смешивать с любым объектом, предоставляя объекту возможность связывать и запускать пользовательские именованные события.
Существует много способов обработки событий в приложении Backbone, но мы будем использовать два способа:
- Мы будем использовать каталог встроенных событий в тандеме с методом
listenTo
который сообщает объекту прослушивать определенное событие в другом объекте. - Мы также будем делегировать события, используя хэш событий непосредственно внутри представления.
Давайте сначала взглянем на использование хэша событий внутри экземпляров представления.
Использование хэша событий
Вот резюме текущего кода SurfboardView
var SurfboardView = Backbone.View.extend({
tagName: 'tr',
template: _.template($('#surfboard-template').html()),
render: function() {
this.$el.html(this.template(this.model.attributes));
return this;
}
});
Вы заметите, что каждый экземпляр представления обернут в свой собственный элемент tr
Давайте немного отредактируем этот шаблон, включив в него пару кнопок, которые позволят нам добавлять и удалять акции. Нам также нужно обновить разметку для таблицы:
<table class="table">
<thead>
<tr>
<th>Manufacturer</th>
<th>Model</th>
<th>Stock</th>
<th>Add/Remove</th>
</tr>
</thead>
<tbody id="table-body"></tbody>
</table>
<script type="text/template" id="surfboard-template">
<td><%= manufacturer %></td>
<td><%= model %></td>
<td><%= stock %></td>
<td>
<button class="add-one">+1</button>
<button class="minus-one">-1</button>
</td>
</script>
Каждый из наших экземпляров представления теперь должен иметь две кнопки, но в данный момент они ничего не делают. Давайте теперь посмотрим на хэш событий и посмотрим, как мы можем регистрировать щелчки на этих двух кнопках. Хэш событий в Backbone обычно выглядит так:
events: {
'event target': 'callback'
}
event
click
dblclick
mouseover
target
Другими словами, если бы мы указали имя класса, идентификатор или тег, он будет искать первый из этого типа, который соответствует поиску. Вот почему я добавил имена классов к кнопкам в шаблоне. Наконец, callback
Поэтому в нашем случае мы можем добавить следующее в наше представление SurfboardView
events: {
'click .add-one': 'addOne',
'click .minus-one': 'minusOne'
},
addOne: function(e) {
e.preventDefault();
// code to add one to stock here...
},
minusOne: function(e) {
e.preventDefault();
// code to minus one from stock here...
}
Единственное, чего сейчас не хватает — это скрипт для обновления модели.
Обновление модели
Если вы поместите предупреждение или оператор консоли в функции addOne
minusOne
Это пока не очень помогает, поскольку мы должны сообщить об этом действии пользователя обратно в модель. Пока что я просто запретил поведение кнопок браузера по умолчанию. Далее нам нужно вызвать функцию модели (которую нам еще предстоит написать). Эти две модельные функции будут находиться внутри самой модели, оставаясь верными нашей идеологии «разделения интересов». Вот обновленный SurfboardView
var SurfboardView = Backbone.View.extend({
tagName: 'tr',
events: {
'click .add-one': 'addOne',
'click .minus-one': 'minusOne'
},
template: _.template($('#surfboard-template').html()),
render: function() {
this.$el.html(this.template(this.model.attributes));
return this;
},
addOne: function(e) {
e.preventDefault();
this.model.addOne();
},
minusOne: function(e) {
e.preventDefault();
this.model.minusOne();
}
});
Помните, что каждый экземпляр представления содержит ссылку на соответствующий экземпляр модели. Вот почему мы можем получить доступ к функциям модели, используя this.model.FUNCTION
Вернувшись в нашу модель Surfboard
Логика здесь проста, поскольку нам просто нужно взять существующий номер запаса, либо добавить или вычесть один из него, и обновить номер запаса этой модели. Мы уже смотрели, как get
Точно так же мы можем set
Вот обновленный код модели:
var Surfboard = Backbone.Model.extend({
defaults: {
manufacturer: '',
model: '',
stock: 0
},
addOne: function() {
this.set({
stock: this.get('stock') + 1
});
// probably update a database
},
minusOne: function() {
this.set({
stock: this.get('stock') - 1
});
// probably update a database
}
});
В реальных приложениях вы, вероятно, захотите обновить базу данных и здесь, но это выходит за рамки этого руководства.
Теперь, если вы запустите это, вы заметите, что события запускаются, когда мы нажимаем кнопки, и соответствующие значения атрибутов в экземплярах модели обновляются. Ничего не происходит на экране, хотя. Это почему? Ну, мы еще не рендерим связанный вид, поэтому давайте посмотрим на это.
Прослушивание событий на инстанции View
Вот быстрый сценарий. Представьте, что у нас уже было изображение, которое уже было отображено на экране. Затем пользователь взаимодействовал с этим представлением, что вызвало изменение модели. Затем эта модель запускает событие change
Когда эта модель будет изменена, мы хотим перерисовать представление. Как мы это делаем?
Для начала давайте вспомним, что у нас есть функция инициализатора, доступная в нашем экземпляре представления. Мы можем использовать это для добавления слушателя, например так:
initialize: function() {
this.listenTo(this.model, "change", this.render);
}
Наш объект просмотра теперь ожидает изменения объекта модели, и когда он действительно меняется, мы снова вызываем функцию рендеринга, которая обновляет представление.
Угадай, что? Мы в значительной степени там, из-за кода, который мы написали до сих пор. Нажав эти кнопки внутри каждого экземпляра представления, мы не только запускаем ассоциированную функцию обратного вызова, но и запускаем событие изменения. Это означает, что, вставив эту маленькую строчку кода в наше представление, мы можем делать все, что захотим, когда срабатывает событие изменения этой модели. Вот посмотрите на наш обновленный код SurfboardView
var SurfboardView = Backbone.View.extend({
tagName: 'tr',
events: {
'click .add-one': 'addOne',
'click .minus-one': 'minusOne'
},
template: _.template($('#surfboard-template').html()),
initialize: function() {
this.listenTo(this.model, "change", this.render);
},
render: function() {
this.$el.html(this.template(this.model.attributes));
return this;
},
addOne: function(e) {
e.preventDefault();
this.model.addOne();
},
minusOne: function(e) {
e.preventDefault();
this.model.minusOne();
}
});
Теперь все должно работать, и вы должны иметь возможность добавлять и минус акции, как вы! Вот демо и кодовая база, чтобы согласиться с этим:
Заворачивать
И это обертка, ребята! В этом уроке мы добились большого прогресса в Backbone и рассмотрели важность событий и способы их использования. Мы также изучили, как работают линии связи между представлениями и моделями, и как мы можем сохранить наш код организованным, чтобы сделать его более перспективным и масштабируемым. Backbone может предложить гораздо больше, чем эта, и документация полна интересных событий, методов и возможностей. Если у вас есть какие-либо вопросы или комментарии, я был бы рад услышать их в обсуждении ниже. Продолжайте изучать и строить, и спасибо за чтение.
Хотите узнать больше о Backbone?
У SitePoint Premium появился новый курс по Backbone.js. Присоединитесь к Premium, чтобы получить к нему доступ, и ко всей библиотеке ресурсов SitePoint!