В красном углу весом всего 29 Кб (без сжатия) находится knockout.js ; чистая библиотека JavaScript, которая упрощает создание динамических пользовательских интерфейсов. Knockout не зависит от библиотеки, поэтому его можно легко использовать с любой из самых популярных библиотек JavaScript, уже доступных, но он особенно хорошо работает с jQuery и использует jQuery.tmpl в качестве движка шаблонов по умолчанию.
Knockout не является заменой jQuery.
Knockout не предназначен для замены jQuery; jQuery пользуется огромной популярностью, так как вы все знаете, я сам его большой поклонник, и он очень хорош в том, что делает. Но сложно создавать сложные пользовательские интерфейсы, используя только jQuery; чем больше приложение за интерфейсом и чем больше пользователь может с ним взаимодействовать, тем сложнее становится поддерживать некое подобие порядка. Обработчики событий имеются в большом количестве, и вы быстро получаете буквально сотни строк кода.
Вполне возможно создать сложные и высокодинамичные пользовательские интерфейсы только с помощью jQuery, но есть ли в бюджете вашего проекта время, необходимое для написания и отладки более 800 строк кода? Как быть через 6 месяцев, когда что-то нужно изменить или добавить? Это где нокаут приходит.
обзор
В этом руководстве мы создадим простой интерфейс, который отображает список контактов, а затем позволяет посетителю взаимодействовать с пользовательским интерфейсом, чтобы изменять способ отображения данных, например фильтровать список или сортировать его. Мы будем использовать нокаут как слой между нашими данными и страницей, чтобы упростить создание и управление или наш пользовательский интерфейс.
Раунд 1 — Начало работы
Knockout использует архитектуру модели View-model-view. Видимый список контактов, которые мы используем в этом примере, и элементы на странице, из которой они состоят, можно рассматривать как представление. Данные, которые отображаются на странице, являются моделью. Модель представления представляет собой представление текущего состояния пользовательского интерфейса, комбинацию данных и представление, которое также содержит поведение, используемое для взаимодействия с моделью и обновления представления.
Давайте начнем с создания структуры папок, которая нам понадобится, и базовой страницы, с которой мы будем работать. Создайте новую папку с именем knockout где-нибудь в вашей системе, затем в этой папке создайте три новые папки с css
, img
и js
. Папка css
будет использоваться для хранения простой таблицы стилей, которую мы будем использовать, а папка img
— отдельное изображение. Папка js
будет содержать файл скрипта, который мы создаем, а также библиотеки, от которых мы зависим. Изначально эта папка должна содержать следующие файлы:
- jquery.tmpl.js
- JQuery-1.6.2.js
- нокаут-1.2.1.js
Теперь в вашем текстовом редакторе создайте следующую базовую страницу:
01
02
03
04
05
06
07
08
09
10
11
12
13
|
<!DOCTYPE html>
<html>
<head>
<title>Knockout</title>
<link rel=»stylesheet» href=»css/styles.css» />
</head>
<body>
<script src=»js/jquery-1.6.2.min.js»></script>
<script src=»js/jquery.tmpl.js»></script>
<script src=»js/knockout-1.2.1.js»></script>
<script src=»js/behavior.js»></script>
</body>
</html>
|
Сохраните эту страницу как index.html
в корневой папке knockout
. Пока что здесь нет ничего заслуживающего внимания, кроме использования HTML5. Хотя knockout.js совместим с более ранними версиями HTML, атрибуты, которые мы будем добавлять к нашим элементам, не являются частью стандартного стандарта HTML 4.01, и поэтому страница будет недействительной. Это не относится к HTML5, который определяет атрибуты data-*
для встраивания пользовательских данных.
Мы также используем базовую таблицу стилей для этого примера, но она используется только для этого конкретного примера и является абсолютно произвольной. Поскольку это не учебник по CSS, я не буду показывать его здесь, но если вам интересно, посмотрите на файл в демоверсии.
Файл Поведения
Далее мы можем создать наш файл поведения; на новой странице в вашем текстовом редакторе добавьте следующий код:
1
2
3
4
5
6
7
8
9
|
(function ($) { var model = [{ name: «John», address: «1, a road, a town, a county, a postcode», tel: «1234567890», site: «www.aurl.com», pic: «/img/john.jpg», deleteMe: function () { viewModel.people.remove(this); }
}, { name: «Jane», address: «2, a street, a city, a county, a postcode», tel: «1234567890», site: «www.aurl.com», pic: «/img/jane.jpg», deleteMe: function () { viewModel.people.remove(this);
}, { name: «Fred», address: «3, an avenue, a village, a county, a postcode», tel: «1234567890», site: «www.aurl.com», pic: «/img/fred.jpg», deleteMe: function () { viewModel.people.remove(this);
}, { name: «Freda», address: «4, a street, a suburb, a county, a postcode», tel: «1234567890», site: «www.aurl.com», pic: «/img/jane.jpg», deleteMe: function () { viewModel.people.remove(this);
}], viewModel = { people: ko.observableArray(model),
}
};
})(jQuery);
|
Сохраните этот файл под именем behavior.js
в папке js
. Мы начнем с определения функции, вызывающей себя, которую мы передаем jQuery, чтобы присвоить псевдониму символ $
.
Затем мы определяем модель, которую будем использовать. В этом примере это локальный массив, но мы можем достаточно легко получить точно такой же формат данных из веб-службы. Наш array
содержит серию объектов людей, которые соответствуют отдельным записям в базе данных contacts
. В основном наши данные состоят из простых строк, но каждый object
также содержит method
deleteMe
, который используется для удаления object
из viewModel
.
Помните, что viewModel
ссылается на текущее состояние пользовательского интерфейса. Это объект, и первым элементом, который мы добавляем к нему, является наш array
содержащий объекты людей. Мы используем метод knockout ko.observableArray()
чтобы добавить наш array
в object
viewModel
. Наблюдаемые объекты являются фундаментальным аспектом knockout.js; мы инструктируем нокаут, чтобы другие лица могли наблюдать за этими предметами и реагировать на их изменения.
Это все наше представление, которое содержит модель на данный момент, хотя мы оставили висячую запятую после значения свойства people, когда добавляем больше свойств.
После object
object
мы используем метод ko.applyBindings()
чтобы применить любые созданные нами привязки и начать управление viewModel
. На данный момент в примере мы еще не добавили привязки. Чтобы создать привязки между нашим view
и viewModel
, нам нужно добавить еще немного HTML.
Раунд 2 — Создание представления
Knockout прекрасно работает с шаблонизацией jQuery.
Теперь у нас есть наша model
и простая viewModel
. Следующее, что мы должны сделать, это отобразить данные из viewModel
на странице. Knockout прекрасно работает с шаблонизацией jQuery. Это позволяет нам использовать плагин tmpl для создания необходимого HTML. Добавьте следующий код в элемент <body>
страницы непосредственно перед элементами <script>
:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
<div id=»people» data-bind=»template: { name: ‘personTemplate’, foreach: people }»>
</div>
<script id=»personTemplate» type=»text/x-jquery-tmpl»>
<section class=»person»>
<img src=»../img/person.png» alt=»${ name }» />
<h1>${ name }</h1>
<address>${ address }</address>
<span class=»tel»>${ tel }
<a href=»http://${ site }» title=»Visit site»>${ site }</a>
<div class=»tools»>
<button data-bind=»click: deleteMe»>Delete</button>
</div>
</section>
</script>
|
Сначала мы добавляем пустой элемент <div>
с id
— в основном для стилизации. Этот элемент также имеет специальный атрибут — data-bind
. Этот атрибут сообщает нокауту, что элемент хранит свои данные в viewModel
. Когда мы вызываем ko.applyBindings()
в нашем JS, это связывание применяется. В этом случае мы используем привязку шаблона, которая позволяет нам указать имя шаблона, которое мы хотели бы использовать в объекте конфигурации, передаваемом привязке.
Мы также используем свойство foreach
в этом объекте конфигурации и указываем имя наших сотрудников observableArray
в качестве источника наших данных. Мы могли бы использовать стандартный синтаксис tmpl {{each}}
для перебора наших данных о людях, но вместо этого более эффективно использовать синтаксис knockout. Поскольку наши данные о людях содержатся в наблюдаемом array
, нокаут отслеживает изменения в array
и, когда они происходят, автоматически обновляет любые шаблоны, отображающие данные. Если мы используем синтаксис tmpl, весь наш шаблон будет перерисовываться каждый раз при изменении данных, но когда мы используем свойство foreach
нокаута, только один экземпляр, соответствующий измененному элементу, перерисовывается.
После контейнера <div>
мы определяем наш шаблон. Это делается так же, как обычный шаблон tmpl. В шаблоне мы указываем элементы, которые мы хотели бы повторить для каждого объекта в нашем источнике данных. У нас есть элемент <section>
в качестве контейнера, за которым следует соответствующий элемент для каждого элемента в object
person
. Стоит отметить, что мы можем предоставить привязки в нашем коде шаблона. Мы добавляем атрибут data-bind
к кнопке удаления; на этот раз мы используем привязку click
и указываем имя person
найденного в каждом object
person
.
Когда мы запускаем страницу в браузере, мы должны обнаружить, что наша страница содержит данные из нашей viewModel
, красиво отрисованные с использованием нашего шаблона:
Так что это круто, верно? Но это не так уж отличается от использования плагина tmpl.
Действительно здорово, что представление не только обновляется соответствующим образом при изменении
viewModel
, но иviewModel
обновляется при изменении представления. Поэтому, если мы нажмем одну из кнопок удаления на нашей странице, изarray
people
также будет удален соответствующийobject
person
!
Исходный array
который мы передали в метод ko.observable()
фактически не обновляется, но обычно мы, вероятно, получаем наши данные из запроса AJAX, а не жестко кодируем их на странице, поэтому все, что нам нужно чтобы сделать это повторно отправить данные, с удаленным person
.
Раунд 3 — Добавление новых данных
У нас есть возможность удалить object
person
; затем мы можем добавить возможность добавить нового человека в нашу dataModel
; Обновите контейнер <div>
мы добавили на страницу ранее, чтобы он содержал следующие новые элементы:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
<a href=»#» title=»Add new person» data-bind=»click: showForm, visible: displayButton»>Add person</a>
<fieldset data-bind=»visible: displayForm»>
<div class=»details»>
<label>Name: <input id=»name» /></label>
<label>Address: <input id=»address» /></label>
<label>Tel: <input id=»tel» /></label>
<label>Site: <input id=»site» /></label>
<div>
<div class=»img»>
<label>Picture: <input id=»pic» type=»file» /></label>
</div>
<div class=»tools»>
<button data-bind=»click: addPerson»>Add</button>
<button data-bind=»click: hideForm»>Cancel</button>
</div>
</fieldset>
|
Первый новый элемент, который мы добавляем, — это тег <a>
, который используется для открытия формы, которая будет принимать новые данные. Это похоже на то, как мы делали бы это в обычной реализации jQuery, за исключением того, что нам также нужно было бы добавить обработчик событий, чтобы прослушивать щелчки на элементе и делать такие вещи, как остановка события. С нокаутом нам не о чем беспокоиться. Все, что нам нужно сделать, это указать имя method
в нашей viewModel
, которое мы хотели бы выполнять при каждом щелчке элемента. Knockout прикрепит обработчик и остановит переход по ссылке для нас.
Как видите, мы можем указать несколько привязок для элемента. Наш элемент <a>
также использует видимую привязку. Опять же, мы указываем свойство нашего viewModel
, за исключением того, что на этот раз это не функция, а простая переменная, содержащая boolean
; вы увидите, как это работает, когда мы вскоре добавим JS для нашей новой функциональности.
После ссылки мы также добавляем <fieldset>
содержащий метки и входные данные, которые мы можем использовать для добавления соответствующих данных для создания нового object
в нашем array
. В конце нашего нового HTML мы добавляем два новых элемента <button>
; к обоим из них добавлены привязки кликов. Первая ссылка на method
addPerson
, вторая на method
hideForm
. Загрузка изображения на самом деле не работает в этом примере, она только для демонстрации.
Теперь давайте посмотрим на новый JavaScript, который нам нужен; Добавьте следующий код непосредственно после свойства viewModel
нашей viewModel
(мы оставили висячую запятую, готовую для добавления этих новых свойств и методов):
1
2
3
4
5
|
displayButton: ko.observable(true), displayForm: ko.observable(false), showForm: function () { viewModel.displayForm(true).displayButton(false);
}, hideForm: function () { viewModel.displayForm(false).displayButton(true);
}, addPerson: function () { viewModel.displayForm(false).displayButton(true).people.push({ name: $(«#name»).val(), address: $(«#address»).val(), tel: $(«#tel»).val(), site: $(«#site»).val(), pic: «», deleteMe: function () { viewModel.people.remove(this); }
});
}
|
Первое свойство — displayButton
, которое является наблюдаемым свойством (его значение может наблюдаться) другими объектами. Объект, который наблюдает за его значением, является нашим элементом <a>
в представлении. Сначала мы установили для него значение true
, поэтому при загрузке страницы (точнее, при applyBindings()
метода applyBindings()
) ссылка будет видна.
Следующее свойство называется displayForm
, которое также является наблюдаемым, за исключением того, что на этот раз мы установили его в false
, поэтому элемент в нашем представлении, который его наблюдает (набор fieldset
), будет изначально скрыт.
Затем мы добавляем два метода: showForm()
и hideForm()
. Эти два простых метода, очевидно, используются, чтобы, соответственно, отображать или скрывать форму, и для этого все, что им нужно, это установить для displayForm
observable значение true
или false
. Поскольку значение соблюдается, при каждом изменении его значения наше представление будет обновляться автоматически.
Мы также настраиваем свойство showButton
всякий раз, когда изменяется состояние формы. Если fieldset
видим, мы скрываем ссылку, и если мы скрываем fieldset
, кнопка снова становится видимой. Как вы можете видеть, knockout поддерживает связывание, что делает обновление нескольких свойств в нашей viewModel
чрезвычайно простым. Вид должен выглядеть следующим образом, когда форма видима:
Последний метод, который мы добавляем, — это addPerson()
, который используется для обновления нашей viewModel
информацией о новом человеке. Все, что мы делаем в этом методе — это скрываем форму и показываем кнопку, а также создаем литерал объекта, содержащий значения, введенные в текстовые поля, а затем помещаем этот object
в наш array
people
.
Чтобы извлечь обновленный people array
из нашей viewModel
, мы можем использовать встроенный в JSON сериализатор JSON для записи наблюдаемого array
в object
JSON. Обычно мы делаем это для того, чтобы передать данные обратно на сервер, но, чтобы проверить это, мы могли бы добавить эту строку кода в конец метода addPerson()
:
1
|
console.log(ko.toJSON(viewModel.people));
|
Метод ko.toJSON()
сгенерировать object
JSON, содержащий текущее содержимое array
people
, который мы видим в Firebug (доступны другие исследователи DOM):
Обзор после боя
В этом уроке мы рассмотрели два основных аспекта knockout.js — декларативные привязки и наблюдаемые.
Привязки применяются в нашем HTML и определяют свойства и массивы данных, значения которых должны соблюдаться при изменениях. Когда эти значения изменятся, элементы в представлении, которые их наблюдают, будут обновлены автоматически, либо применяя новую итерацию шаблона, либо показывая или скрывая элемент, как в этом примере.
Есть и другие привязки, которые мы также можем использовать для выполнения различных действий при взаимодействии с представлением или при обновлении данных в viewModel
.
Knockout.js — чрезвычайно полезный уровень, который находится между интерфейсом нашего пользовательского интерфейса и его основными данными и управляет взаимодействиями и изменениями состояния для нас. Это делает для нас очень много работы, хотя в этом базовом примере мы на самом деле только поверхностно продемонстрировали, на что он способен. Что вы думаете о knockout.js?