Доброго времени суток, уважаемые хабровчане. Некоторое время назад, я познакомился с библиотекой RivetsJS. Она мне пришлась по вкусу, хотя содержит в себе только инструменты для data-binding. Сразу хочу сказать, что даже не собираюсь спорить, для этих вещей есть замечательные AngularJS и др. фреймворки, но лично я не вижу смысла подключать такой мощный инструмент, как Angular, если мне требуется только малая толика его возможности. Поэтому, для этих целей, я выбрал именно RivetsJS. И вот, воодушевленный идеей перевести его документацию на русский, я пишу эту статью. Мотив простой — рассказать об этой библиотеке, и я не нашел ничего лучше, чем просто перевести её документацию, которая, возможно только на мой взгляд, написана на немного «ломаном» английском. Итак, поехали.
P.S. Сразу попрошу прощения, это мой первый перевод, возможно я не супер-пупер переводчик. Если вам что-то покажется нелепым или вы найдете ошибку — прошу сообщить об этом мне, я сразу же её исправлю. Заранее, спасибо.
Документация RivetsJS
Гайд
- Установка
- Использование
- Биндинг
- Форматеры
- Компоненты
- Адаптер
- Свойства
- Итерации биндинга
Справочник
- text
- html
- show
- hide
- enabled
- disabled
- if
- unless
- value
- checked
- unchecked
- on-[event]
- each-[item]
Гайд
Установка
Вы можете скачать последнюю стабильную версию на GitHub, или установить используя менеджер пакетов по вашему выбору. В настоящее время мы поддерживаем релизы на на npm, component, jam, bower (рекомендуется).
bower install rivets
Rivets имеет тесную зависимость с Sightglass. Если вы хотите подключить Sightglass отдельно, убедитесь что подключили его первым.
<script src="bower_components/sightglass/index.js"></script>
<script src="bower_components/rivets/dist/rivets.min.js"></script>
В качестве альтернативы, в можете подключить пакет Rivets, который содержит обе библиотеки.
<script src="bower_components/rivets/dist/rivets.bundled.min.js"></script>
Примечание: CommonJS модуль и загрузчики скриптов AMD такие как RequireJS и almond полностью поддерживаются, если это вам потребуется.
Использование
Шаблоны
Шаблон формируют ваш UI в обычном HTML. Вы можете определить их непосредственно в документе, используя элементы шаблонизатора, или хранить и загружать их так, как вам нравится. Просто используйте удобный для вас способ вывода данных в шаблон.
<section id="auction">
<h3>{ auction.product.name }</h3>
<p>Текущая ставка: { auction.currentBid | money }</p>
<aside rv-if="auction.timeLeft | lt 120">
Поторопись! До окончания аукциона осталось { auction.timeLeft | time }.
</aside>
</section>
Важными элементами в данном примере являются добавление атрибута с префиксом «rv-», а также выделение и обертывание участков в "{...}". Эти элементы являются объявлением биндинга, и это способ передачи ваших данных в шаблоны. Все объявления используют короткий и выразительный синтаксис.
(keypath | примитив) [Форматтеры...]
Keypath'ы получают данные наблюдения и пересматривают биндинг, когда происходят какие-либо изменения. Примитивами могут быть string, number, boolean, null или undefined.
Поподробнее о форматерах, о их свойствах и возможностях будет рассказано в соответствующей главе.
Биндинг
Просто вызовите rivets.bind на элементе в шаблоне и передайте ему те данные, которые надо вывести.
rivets.bind($('#auction'), {auction: auction})
При каждом вызове rivets.bind возвращается полный набор предстваления данных. Используйте view.unbind(), для того чтобы остановить событие биндинга.
Конфигурирование
Используйте rivets.configure чтобы задать настройки приложению. Учтите, что если потребуется, все настройки могут быть заменены локально на определенном представлении.
rivets.configure({
// Префикс атрибута в представлении
prefix: 'rv',
// Предзагрузка шаблона с инициализации дата-байдинга.
preloadData: true,
// Root sightglass interface for keypaths
rootInterface: '.',
// Шаблонные разделители для встраивания в текст
templateDelimiters: ['{', '}'],
// Дополнительный обработчик событий on-* binder
handler: function(target, event, binding) {
this.call(target, event, binding.view.models)
}
})
Биндеры
Биндеры — это наборы инструкций, которые говорят Rivets.js, как обновлять DOM, когда наблюдаемые свойства изменяются. Rivets.js поставляется в комплекте с часто используемыми Биндерами для вашего удобства. Смотрите «Справочник», чтобы узнать как использовать встроенные биндеры, которые идут «в коробке» с Rivets.js.
Сначала вы можете выполнять основные UI задачи с помощью встроенных биндеров, но настоятельно рекомендуется расширить Rivets.js с вашими собственными биндерами, которые являются специфическими для исполнения задач вашего приложения.
Односторонние биндеры
Односторонние биндеры обновляют DOM, когда свойства модели изменяются (только модель-вид). Скажем, нам нужен просто биндер, который обновляет цвет элемента, когда свойства модели меняются. Здесь мы можем определить односторонний биндер простым замыканием. Функция принимает элемент и текущее значение свойства модели, которое мы будем использовать для обновления свойства элемента.
rivets.binders.src = function(el, value) {
var href = "/my/path/to/image/" + value
el.setAttribute("src ", src);
}
rivets.binders.alt = function(el, value) {
var alt = value
el.setAttribute("alt", alt);
}
С определенными биндерами выше, вы можете использовать "rv-src" и “rv-alt” в вашем шаблоне.
<img rv-src="data.image" rv-alt=”data.title” />
Двусторонние биндеры
Двусторонние биндеры, как и односторонние, могут обновлять DOM, когда свойство модели меняется (модель-вид), но также может обновлять модель, когда пользователь взаимодействует с DOM (вид-модель), такие как обновление личного кабинета, введя данные для авторизации и нажав submit, или же взаимодействие со сторонними виджетами.
Для того, чтобы обновить модель, когда пользователь взаимодействует с DOM, вы должны сказать Rivets.js, как забиндить и разбиндить этот DOM элемента, чтобы установить значение в данной модели. Вместо объявления биндера как простой функции-замыкания, двусторонние биндеры объявляются как объекты, содержащие несколько дополнительных функций.
rivets.binders.toggle = {
bind: function(el) {
adapter = this.config.adapters[this.key.interface]
model = this.model
keypath = this.keypath
this.callback = function() {
value = adapter.read(model, keypath)
adapter.publish(model, keypath, !value)
}
$(el).on('click', this.callback)
},
unbind: function(el) {
$(el).off('click', this.callback)
},
routine: function(el, value) {
$(el)[value ? 'addClass' : 'removeClass']('enabled')
}
}
API
binder.bind
Данная функция вызывается для инициализации view.bind().Используйте её для хранения начального состояния биндинга, или чтобы назначить слушателей событий в элементе.
binder.unbind
Данная функция вызывается для инициализации view.unbind(). Используйте её, чтобы сбросить любое состояния элемента, который был бы изменен binder.routine, или отвязать любые слушатели событий на элемент, который вы установили в функции binder.bind.
binder.routine
Шаблонная функция вызывается когда наблюдаемый атрибут модели меняется и используется для обновления DOM. Когда объявляется односторонний биндинг как простая функция-замыкание, она на самом деле и является шаблонной функцией(routine function), которую вы определяете.
binder.publishes
Установите значение true, если хотите вызвать view.publish() и опубликовать эти биндинг.
Форматеры
Форматеры — это функции, которые видоизменяют входящие и/или исходящие значения биндинга. Вы можете использовать их для форматирования дат, чисел, валют и.т.д. Форматеры бывают очень полезны, когда вам необходимо обработать выводимые данные, и чтобы не грузить сервер – можно погрузить клиент. К примеру, вы получаете от сервера дату в формате timestamp. При помощи форматера прямо на клиенте timestamp можно преобразовать в нужный формат даты написав всего одну функцию и использовать её во всем вашем биндинге
Односторонние форматеры
Это, безусловно, наиболее распространенные и практичные способы использования форматирования — простые видоизменения значения только для чтения. В предоставленном примере ниже, мы можем определить форматер количества дней, оставшихся до окончания какого либо события. Допустим, до окончания события осталось 3ое суток.
rivets.formatters.remain = function(value){
value = parseInt(value / 86400);
return value;
}
Форматтеры применяются при объявлении биндинга, символ "|" применяется в качестве разделителя.
<span rv-text="event.endDate | remain"></span>
Двусторонние форматеры
Двусторонние форматеры полезны, когда вы хотите хранить значения в конкретном формате, например как стоимость в долларах, но одновременно дать возможность пользователю вводить данные в другом формате, например в рублях.
Вместо объявления форматера как простой функции-замыкания, вы можете объявить её как объект, содержащий функции read и publish. Когда форматер объявлен как простое замыкание, Rivets предполагает что это будет только для чтения, но когда форматтер объявлен как объект, Rivets использует функции read и publish для эффективной сериализации и обратной конвертации значений.
Скажем, мы хотим хранить денежное значение в долларах, но пусть пользователь вводит это значение в рублях, а форматтер автоматически конвертирует эти значения по стоимости на валютном рынке(опустим тот момент как эта стоимость туда подгружается) при занесении этого значения в модель. Как раз для этой цели, мы будем использовать двусторонний форматтер валют.
rivets.formatters.currency = {
read: function(value) {
return (value / 50)
},
publish: function(value) {
return Math.round(parseInt(value) * 50)
}
}
Теперь вы можете сделать биндинг, используя форматер с любым типом биндинга — односторонним или двусторонним.
<input rv-value="item.price | currency">
Обратите внимание, что вы также можете привязать двунаправленные форматеры с любыми другими, и причем в любом порядке. Они читаются с лево на право, и публикуются с право на лево, пропуская любые форматеры, предназначенные только для чтения, когда публикует(вставляет) данные обратно в модель.
Аргументы Форматеров
Форматеры могут принимать любое количество аргументов в виде keypath'ов и примитов.
Форматтеры могут разделять значения используя символ "|".
Аргументами форматтера могут быть keypath'ы и примитивы. Keypath'ы в качестве аргументов форматтера наследуют все свойства и возможносты обычных keypath'ов.
(Форматеры) [keypath | Примитив...]
<span>{ alarm.time | time user.timezone 'hh:mm' }</span>
Значение каждого аргумента в объявлении биндинга будет оцениваться и передается в функцию-форматер в качестве дополнительного аргумента.
rivets.formatters.time = function(value, timezone, format) {
return moment(value).tz(timezone).format(format)
}
Компоненты
Компоненты позволяют вам определять многоразовые виды, которые могут быть использованы в любом из ваших шаблонов. Это полезно использовать там, где компоненты нужно встроить в ваш шаблон в связи с биндерами; Биндеры определяют ваши собственные атрибуты, в то время как компоненты определяются как собственные элементы.
Объект компонента должен быть определен как функция template, возвращающая шаблон для компонента(это может быть строка HTML, или другой необходимый элемент). Он также должен определять initialize функцию, которая возвращает специальный объект scope для биндинга в шаблон (данная функция имеет некую схожесть с MVC).
rivets.components['todo-item'] = {
// Возвращает шаблон для компонента.
template: function() {
return JST['todos/todo-item']
},
// Берет оригинальный элемент и данные которые проходят через компонент
// (Либо из rivets.init или атрибут элемента шаблона в компоненте.
initialize: function(el, data) {
return new ItemController({
item: data.item
})
}
}
Чтобы использовать компонент внутри шаблона, просто используйте элемент с таким же названием тега как в ключе компонента. Все атрибуты элемента будут расценены как keypath'ы перед вставкой в функцию компонента initialize.
<todo-item item="myItem"></todo-item>
Дополнительно, если вы хотите чтобы определенный атрибут был статичен, вместо keypath'а вы можете задать ему свойство static.
rivets.components['todo-item'] = {
static: ['list-style'],
…
}
<todo-item item="myItem" list-style="condensed"></todo-item>
Компоненты также могут быть инициализированы сами по себе вне шаблона. Это полезно, когда вы хотите сами вставить новый вид в ваш DOM, например как модальное окно, которое появляется при первом открытии вашего приложения. Данное API схоже с rivets.bind, исключая того, что вместо передачи текущего шаблона\элемента, вы передаете только имя компонента и его класс\идентификатор\тег родительского элемента, в котором вы хотите отрендерить данный шаблон компонента.
rivets.init('my-app', $('body'), {user: user})
rivets.init('todo-item', $('#modal-content'), {item: myItem})
Адаптеры
Rivets.js является агностиком по отношению к объектам и их описанию. Это делает его очень гибким, и он может подстраиваться и адаптироваться под разработку вместе с другими библиотеками и фрейморками, но это также значит, что вы должны сказать Rivet.js как он должен описывать те или иные объекты. Данная фича управляется библиотекой Sightglass.
Каждый адаптер определяется как уникальный интерфейс(одиночный символ), который используется для разделения ключей и keypath'ов. Интерфейсы, используемые в keypath'ах, определяют какой адаптер использовать для каждого промежуточного ключа.
user.address:city
В примере указанном выше, keypath использует адаптер "." для доступа к ключу address объекта user, и адаптер ":" для доступа к ключу city объекта address. Представьте на секунду, что address это нормальное, свойство объекта user указывающий на модель Backbone, но city на самом деле это атрибут модели Backbone, вы можете увидеть, насколько этот вид обозначения на самом деле лаконичен и выразителен.
Встроенный адаптер
Rivets.js поставляется с адаптером "." описывающим свойства простых Javascipt объектов. Данный адаптер уже реализован в ES5(ECMAScript 5), на примере Object.defineProperty. В будущем, данный адаптер будет реализован, используя исключительно Object.observe, как только браузеры начнут его поддерживать.
Если вам нужно использовать браузеры без поддержки ES5(
Создание адаптера.
Адаптеры определяются с помощью rivets.adapters с интерфейсами в качества имени свойства и объектом адаптера в качестве значения.
Следующий адаптер ":" работает и для моделей Backbone.js, и для модулей Stapes.js.
rivets.adapters[':'] = {
observe: function(obj, keypath, callback) {
obj.on('change:' + keypath, callback)
},
unobserve: function(obj, keypath, callback) {
obj.off('change:' + keypath, callback)
},
get: function(obj, keypath) {
return obj.get(keypath)
},
set: function(obj, keypath, value) {
obj.set(keypath, value)
}
}
Расчетные свойства
Расчетные свойства — это функции, которые получают данные на переоценку в случае когда одно или более зависимых свойств меняется. Объявляются расчетные свойства в Rivets.js довольно просто, просто разделите функцию от её зависимости с помощью знака "
<span rv-text="event.duration < start end"></span>
Обратите внимание, что зависимый keypath берется из целевого объекта, а не из контекста модели. Так для примера указанного выше, целевым является объект event, с зависимостями event.start и event.end.
Итерации биндинга.
Используйте rv-each-[item] биндер, который представлен в Rivets.js, для того чтобы вывести циклом все элементы массива и добавить связанные экземпляры этого элемента.
<ul>
<li rv-each-todo="list.todos">
<input type="checkbox" rv-checked="todo.done">
<span>{ todo.summary }</span>
</li>
<ul>
Справочник
text
Вывод текста в элемента
<h1 rv-text="user.name"></h1>
Вы также можете вывести текст, используя интерполяцию.
<p>{ user.name } is { user.age } years old.</p>
html
Вывод HTML содержимое в элемент.
<section rv-html="item.summary"></section>
show
Вывод элемента в зависимости от значения — если true, то вывод, если false, то скрыть.
<button rv-show="user.admin">Remove</button>
hide
Обратный атрибут rv-show, если значение установлено true, то скрыть, если false, то вывести.
<section rv-hide="feature.disabled"></section>
enabled
Атрибут, задающий активность элементам. True — элемент enabled, false — disabled.
<button rv-enabled="user.canVote">Upvote</button>
disabled
Атрибут обратный rv-enabled. True — disabled, false — enabled.
<button rv-disabled="user.suspended">Upvote</button>
if
Вставляет и биндит элемент, а также узлы(nodes) в DOM, когда значение равно true, и не выводит элементы когда значение расценено как false.
<section rv-if="item.editable"></section>
unless
Атрибут обратный rv-if.
<section rv-unless="item.locked"></section>
value
Устанавливает значение элемента, когда изменяется атрибут, и устанавливает значение связанного объекта, когда элемент input изменяется от пользовательского ввода (работает в обе стороны).
<input rv-value="item.name">
checked
Легко догадаться, предназначено для и . Устанавливает атрибут «checked», когда значение расценено как true, и убирает атрибут «checked», когда значение расценено как false. Также устанавливает значение связанного объекта, когда элемент чекбокс изменяется от пользовательского ввода(работает в обе стороны).
<input type="checkbox" rv-checked="item.enabled">
unchecked
Атрибут, обратный rv-checked.
<input type="checkbox" rv-unchecked="item.disabled">
on-[event]
Биндит слушателя событий на элемент, используя событие указанное в [event] и cвязанный объект(должен возвращать функцию) как обратный вызов (callback)
Примечание: Если конечное значение биндинга меняется на другую функцию, данный биндер автоматически разбиндит старый колбэк и забиндит нового слушателя событий на новую функцию.
<button rv-on-click="item.destroy">Remove</button>
each-[item]
Добавляет новый экземпляр элемента для каждого элемента в массиве. Каждый элемент связан с совершенно новым вложенным видом, который содержит дополнительное свойство, который указывает на текущий итерируемый элемент массива.
<ul>
<li rv-each-todo="todos">
<input type="checkbox" rv-checked="todo.done"> { todo.name }
</li>
<ul>
P.S. Если к данной документации проявится интерес, я дополню её «живыми» примерами.
This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.