Добро пожаловать во вторую часть использования Backbone внутри администратора WordPress. В первой части мы настроили «бэкэнд» нашего плагина, а теперь во второй части мы закончим добавлением функциональности «на стороне клиента» или «клиентской части». Для обзора того, что мы создаем в этом руководстве, а также структуры папок и файлов, просмотрите первую часть.
1. Создайте файл шаблона
В папке src создайте еще один файл с именем Templates и файл внутри этого файла с именем metabox.templ.php . Здесь мы будем помещать HTML-код, необходимый для нашего мета-блока. Это также отличная возможность вывести данные JSON, необходимые для наших ответов.
Ваши папки и файлы теперь должны выглядеть следующим образом.
Создать шаблон для одного ответа
Давайте еще раз посмотрим на то, что мы создаем. Вы можете думать о каждом ответе как о модели данных, и поскольку мы будем использовать шаблоны на стороне клиента для создания представления для каждого из них, это представление может реагировать на изменения в модели. Это позволяет нам быть очень конкретными при привязывании событий к пользовательскому интерфейсу и, естественно, приводит к упрощению рабочего процесса — как только вы разберетесь в этом, то есть.
Внутри нашего недавно созданного metabox.templ.php это шаблон, который мы будем использовать для каждой из наших моделей. Вы можете видеть, что мы в основном оборачиваем некоторый HTML в тег скрипта. Мы даем тегу script атрибут type="text/template"
чтобы браузер не отображал его на странице. Этот небольшой фрагмент HTML-кода будет использоваться позже для создания разметки, необходимой для каждого представления. Мы будем использовать встроенные в Underscore возможности шаблонов, поэтому значения обернуты так: будут заменены данными в наших моделях позже.
1
2
3
4
5
6
7
8
9
|
<!— src/templates/metabox.templ.php —>
<!— Template —>
<script type=»text/template» id=»inputTemplate»>
<label for=»<%= answer_id %>»><%= index %>:</label>
<input id=»<%= answer_id %>» class=»answers» size=»30″ type=»text» name=»<%= answer_id %>» value=»<%= answer %>» placeholder=»Answer for Question <%= index %> Here»>
<button disabled=»true»>Save</button>
</script>
<!— End template —>
|
Базовый HTML
Все еще внутри src / templates / metabox.templ.php — здесь мы просто устанавливаем контейнеры, которые будут заполнены входными данными из шаблона выше. Это происходит после того, как Backbone проанализировал данные JSON, необходимые для модели, поэтому пока это все, что нам нужно сделать здесь.
1
2
3
4
5
6
7
8
9
|
<!— src/templates/metabox.templ.php —>
<p>Enter the Answers below</p>
<div id=»answerInputs»></div>
<div id=»answerSelect»>
<span>Correct Answer:
</div>
<p>
<input name=»save» type=»submit» class=»button button-primary button-small» value=»Save all»>
</p>
|
Выведите JSON
Последнее, что нужно в файле src / templates / metabox.templ.php , — это данные JSON, представляющие каждый ответ. Здесь мы создаем объект в глобальном пространстве имен, а затем присваиваем значения, которые мы отправили, с $viewData
массива $viewData
. Мне также нравится сохранять ссылки на контейнеры, которые мы будем использовать позже, чтобы у меня не было идентификаторов в двух отдельных файлах.
01
02
03
04
05
06
07
08
09
10
11
|
<!— src/templates/metabox.templ.php —>
<script>
window.wpQuiz = {};
var wpq = window.wpQuiz;
wpq.answers = <?= $answers ?>;
wpq.answers.correct = <?= $correct ?>;
wpq.answerSelect = ‘#answerSelect’;
wpq.answerInput = ‘#answerInputs’;
wpq.inputTempl = ‘#inputTemplate’;
wpq.post_id = <?= $post->ID ?>;
</script>
|
2. JavaScript
Хорошо, если вы зашли так далеко, вы успешно настроили свой плагин, чтобы разрешить использование Backbone.js, а ваш мета-блок выводит необходимую разметку и данные JSON. Теперь пришло время собрать все вместе и использовать Backbone.js для организации нашего клиентского кода. Пришло время покрыть:
- Создание коллекции моделей из данных JSON
- Использование клиентских шаблонов для создания представления для каждого
- Наблюдение за кликом, ключом и размытием событий в каждом представлении
- Сохранение модели обратно в базу данных
Создайте файл admin.js и поместите его в папку js
Ваша окончательная структура каталогов и файлов должны выглядеть следующим образом.
Прежде всего, мы обернем все, что мы делаем, в функцию с немедленным вызовом и передадим jQuery для использования со знаком $
, я не буду показывать эту обертку в каких-либо других фрагментах, поэтому убедитесь, что вы поместили все ниже в нее.
1
2
3
4
5
6
7
|
/* js/admin.js */
(function($) {
/** Our code here **/
}(jQuery));
|
Затем нам нужно получить доступ к нашим данным, хранящимся в глобальном пространстве имен, а также создать новый объект, который будет хранить наши объекты Backbone.
1
2
3
4
|
/* js/admin.js */
var Quiz = { Views:{} };
var wpq = window.wpQuiz;
|
Модель
Модель представляет собой один ответ. В его конструкторе мы делаем несколько вещей.
- Установка значения по умолчанию для правильного как
false
- Установка URL, который необходим Backbone для сохранения модели обратно в базу данных. Мы можем получить доступ к правильному URL-адресу благодаря WordPress, который доказывает
ajaxurl
переменнойajaxurl
, доступной на каждой странице администратора. Мы также добавляем имя нашего метода, который обрабатывает запрос ajax - Затем мы перезаписываем метод
toJSON
чтобы добавить идентификатор текущей записи к каждой модели. Это можно было бы сделать на стороне сервера, но я привел это здесь в качестве примера того, как вы можете переопределить то, что сохраняется на сервере (это может оказаться очень удобным, поэтому я включил его здесь) - Наконец, в методе initialize мы проверяем, является ли текущая модель правильным ответом, сравнивая ее идентификатор с идентификатором правильного ответа. Мы делаем это так, чтобы позже мы знали, какой ответ должен быть выбран по умолчанию
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
/* js/admin.js */
Quiz.Model = Backbone.Model.extend({
defaults : {
‘correct’ : false
},
url : ajaxurl+’?action=save_answer’,
toJSON : function() {
var attrs = _.clone( this.attributes );
attrs.post_id = wpq.post_id;
return attrs;
},
initialize : function() {
if ( this.get( ‘answer_id’ ) === wpq.answers.correct ) {
this.set( ‘correct’, true );
}
}
});
|
Коллекция
Коллекция, по сути, является просто оберткой для множества моделей и делает работу с этими моделями проще простого. Для нашего небольшого примера мы не будем изменять коллекцию, кроме указания, какую модель она должна использовать.
1
2
3
4
5
|
/* js/admin.js */
Quiz.Collection = Backbone.Collection.extend({
model: Quiz.Model
});
|
Обертка Inputs
Наше первое представление может рассматриваться как оболочка для отдельных полей ввода. Нам не нужно объявлять шаблон или какой HTML-элемент мы хотим, чтобы Backbone создавал для нас в этом случае, потому что позже, когда мы создадим это представление, мы передадим ему идентификатор div
который мы создали в мета-боксе. файл. Затем Backbone просто использует этот элемент в качестве своего контейнера. Это представление будет принимать коллекцию, и для каждой модели в этой коллекции он будет создавать новый элемент input
и добавлять его к себе.
01
02
03
04
05
06
07
08
09
10
11
|
/* js/admin.js */
Quiz.Views.Inputs = Backbone.View.extend({
initialize:function () {
this.collection.each( this.addInput, this );
},
addInput : function( model, index ) {
var input = new Quiz.Views.Input({ model:model });
this.$el.append( input.render().el );
}
});
|
Один вход
Этот следующий вид представляет одну модель. В целях демонстрации типов вещей, которые вы можете делать при кодировании JavaScript таким образом, я попытался предоставить несколько различных методов взаимодействия и показать, как реагировать на них с помощью Backbone.
Обратите внимание, что мы указываем здесь « tagName
» вместе с шаблоном. В нашем случае это будет захватить тот шаблон, который мы рассматривали ранее, проанализировать его, используя данные из модели, а затем обернуть все в тег p
(что даст нам небольшой запас по каждому из них).
Также обратите внимание, как события связаны с элементами в представлении. Гораздо чище, чем ваш обычный обратный вызов jQuery, и что еще лучше, это возможность использовать селектор jQuery, подобный this.$('input')
в наших представлениях, зная, что они автоматически ограничиваются областью представления. Это означает, что jQuery не смотрит на весь DOM при попытке сопоставить селектор.
С этой точки зрения мы сможем:
- Знать, когда поле ввода было изменено
- Обновите модель, связанную с ней автоматически (которая будет использоваться для автоматического обновления поля выбора под ним)
- Включить кнопку сохранения на стороне входа, который был изменен
- Выполните сохранение обратно в базу данных
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
45
|
/* js/admin.js */
Quiz.Views.Input = Backbone.View.extend({
tagName: ‘p’,
// Get the template from the DOM
template :_.template( $(wpq.inputTempl).html() ),
// When a model is saved, return the button to the disabled state
initialize:function () {
var _this = this;
this.model.on( ‘sync’, function() {
_this.$(‘button’).text( ‘Save’ ).attr( ‘disabled’, true );
});
},
// Attach events
events : {
‘keyup input’ : ‘blur’,
‘blur input’ : ‘blur’,
‘click button’ : ‘save’
},
// Perform the Save
save : function( e ) {
e.preventDefault();
$(e.target).text( ‘wait’ );
this.model.save();
},
// Update the model attributes with data from the input field
blur : function() {
var input = this.$(‘input’).val();
if ( input !== this.model.get( ‘answer’ ) ) {
this.model.set(‘answer’, input);
this.$(‘button’).attr( ‘disabled’, false );
}
},
// Render the single input — include an index.
render:function () {
this.model.set( ‘index’, this.model.collection.indexOf( this.model ) + 1 );
this.$el.html( this.template( this.model.toJSON() ) );
return this;
}
});
|
Выбор элемента
Этот элемент выбора — то, где пользователь может выбрать правильный ответ. Когда это представление создается, оно получит ту же коллекцию моделей, что и оболочка ввода. Это пригодится позже, потому что мы сможем прослушивать изменения модели в полях ввода и автоматически обновлять соответствующие значения в этом элементе select.
01
02
03
04
05
06
07
08
09
10
11
|
/* js/admin.js */
Quiz.Views.Select = Backbone.View.extend({
initialize:function () {
this.collection.each( this.addOption, this );
},
addOption:function ( model ) {
var option = new Quiz.Views.Option({ model:model });
this.$el.append( option.render().el );
}
});
|
Один вариант просмотра
Наш окончательный вид создаст элемент option для каждой модели и будет добавлен к элементу select выше. На этот раз я показал, как вы можете динамически устанавливать атрибуты элемента, возвращая хэш из функции обратного вызова, назначенной свойству атрибутов. Также обратите внимание, что в методе initialize()
мы «подписались» на изменение событий в модели (в частности, атрибута answer
). Это в основном просто означает: каждый раз, когда атрибут ответа этой модели изменяется, вызывается метод render()
(который в этом случае просто обновит текст). Эта концепция «подписки» или «прослушивания» событий, происходящих внутри модели, действительно делает Backbone.js и многие другие библиотеки такими мощными, полезными и приятными для работы.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
|
/* js/admin.js */
Quiz.Views.Option = Backbone.View.extend({
tagName:’option’,
// returning a hash allows us to set attributes dynamically
attributes:function () {
return {
‘value’:this.model.get( ‘answer_id’ ),
‘selected’:this.model.get( ‘correct’ )
}
},
// Watch for changes to each model (that happen in the input fields and re-render when there is a change
initialize:function () {
this.model.on( ‘change:answer’, this.render, this );
},
render:function () {
this.$el.text( this.model.get( ‘answer’ ) );
return this;
}
});
|
Создание коллекции и просмотров
Теперь мы так близки, все, что нам нужно сделать, — это создать новую коллекцию и передать ей необходимый JSON, а затем создать оба представления «оболочки» для элемента select и для входных данных. Обратите внимание, что мы также передаем свойство el
нашим представлениям. Это ссылки на элемент div и select, который мы оставили пустым ранее в мета-поле.
1
2
3
4
5
|
/* js/admin.js */
var answers = new Quiz.Collection( wpq.answers );
var selectElem = new Quiz.Views.Select({ collection:answers, el :wpq.answerSelect });
var inputs = new Quiz.Views.Inputs({ collection:answers, el:wpq.answerInput });
|
3. Активируйте плагин
Если вы сделали это до конца, у вас должен быть полностью рабочий пример того, как включить Backbone JS в плагин WordPress. Если вы пойдете дальше и посмотрите на исходные файлы, вы заметите, что фактический объем кода, необходимый для включения Backbone, относительно невелик. Большая часть кода, который мы здесь рассмотрели, была PHP, необходимой для плагина. Ежедневная работа с Backbone в течение последних 6 недель действительно дала мне новое уважение к организации внешнего кода, и я надеюсь, что вы сможете оценить преимущества, которые наверняка получат от такой работы.
В сообществе WordPress я могу представить некоторые из более сложных и высококачественных плагинов, которые действительно выигрывают от использования Backbone, и для меня большая честь поделиться с вами техникой, позволяющей сделать именно это.