
- subscribe
- publish
- unsubscribe
- channels
- register
- registerAll
- startViewOrService
- startPlugin
- stop
- startAll
- getView
- getService
- initialize
- getStartedView
- subscribe
- unsubscribe
- publish
- $
- finish
- getChildren
- bindModel
- changeModel
- unbindModel
- render
- refreshScroll
- onInitialize
- onStartRender
- onEndRender
- onNewExtras
- onStartAttach
- onEndAttach
- onEndDetach
- onDestroy
- onReceiveMsg
- loader.done
О проекте
Фреймворк для быстрого построения мобильных приложений. Оптимизирован для iOS, Android, поддерживается также Chrome, Firefox, Opera, Safari, IE 10, Windows Phone 8.
Общие положения
Чем является RAD.js:
Это фреймворк системного уровня. Он позволяет разрабатывать single-page-приложение, как классическое многостраничное, и берет на себя задачи системного уровня, такие как шину сообщений, создание и уничтожение экземпляров частей приложения, транзакции переходов между views и т.д.
Чем не является RAD.js:
- MV* фреймворком, MV* в нем реализована на базе BackboneJS.
- Фремворком уровня приложения. Приложением может выступать любой JavaScript-объект. Фреймворк не диктует условия, как должен быть построен объект приложения.
- Layout-движком. Layout-движок реализован как плагин navigation к ядру фреймворка.
- UI-фреймворком. Нет смысла писать очередной UI-фреймворк, так как от проекта к проекту в реальной жизни паттерны UI и оформление приложения меняется. В качестве расширения к базовой View, реализованы ScrollableView, PopupView и ToastView, то есть те, которые имеют расширенное и часто встречающееся поведение в разных проектах. Вы сами спокойно можете писать новые View, которые часто встречаются в Ваших проектах, например ListView, и т.д.
Достоинства:
- Оптимизирован для PhoneGap и мобильных браузеров;
- Возможность динамически управлять экземплярами модулей приложения (создавать, уничтожать), как на уровне приложения через функциональность ядра, так и на уровне view;
- Возможность составить приложение из слабо связанных модулей: моделей, views, сервисов (часть приложения без визуального представления) и объекта приложения;
- Древовидная структура сообщений;
- Debug-режим ядра и сообщений;
- Гибкая слабосвязанная архитектура — практически любой сторонний код может быть обернут в модуль несколькими строчками кода. Падение одного из модулей не ведет к падению всего приложения;
- Возможность отслеживания жизненого цикла view и сервисов. Наличие callback методов на все события жизненого цикла;
- Шаблонизация. Шаблоном выступает отдельный HTML-файл, который может быть наверстан отдельно;
- Частичная шаблонизация view. Возможность указать части шаблона view, которые будут/не будут меняться (rerender) при изменении модели;
- Объектом приложения может выступать любой объект JavaScript;
- Возможность расширения функциональности ядра за счет плагинов;
- Сложные вложенные views и декларируемые анимации перехода между ними;
- Возможности наследования views, сервисов и моделей
- Модальные и немодальные самопозиционируемые окна
- Динамическая маршрутизация. Достаточно указать параметр backstack: true, и транзакция между views (новое расположение views на экране) будет занесена в history браузера;
- Повторное использование модулей в других проектах;
- Возможность модульного тестирования, внешними фреймворками.
Объект приложения
В качестве объекта application может выступать любой JS-объект. Он будет доступен в любом модуле(views и сервисах) через this.application.
Например: this.application.logout(); в любом модуле вызовет метод logout у объекта application.
Методы у приложения могут быть любыми - просто желательно, чтобы они отображали функциональность приложения (к примеру - залогиниться, вылогиниться, выйти из приложения и т.д.). То есть, принадлежали к уровню абстракции "приложение",
а не к другим уровням, например, "сеть" или "модели".
Пример объявления конструктора объекта приложения с использование методов ядра:
RAD.application(function (core) { 'use strict'; var app = this; app.start = function () { var options = { container_id: '#screen', content: "view.parent_widget", animation: 'none' }; core.publish('navigation.show', options); }; return app; });
Core
- Управление жизненым циклом частей приложения (позволяет добавлять и удалять модули; фактически контролируют загрузку памяти JavaScript-машины);
- Коммуникационный интерфейс (позволяет частям приложения общаться между собой);
- Возможность дальнейшего расширения функциональности за счет плагинов.
Методы ядра
К примеру, методы ядра могут вызываться так:
// если ядро доступно(из плагина или на уровне application) core.publish('navigation.show', options); // для модуля this.publish('navigation.show', options);
subscribe
subscribe(channel, fn, context);
Подписывает callback-функцию fn на выполнение в контексте context при получении сообщения по каналу channel.
Обрабатывать такие сообщения можно в коллбэке onReceiveMsg.
publish
publish(channel, data)
Публикует сообщение на канале channel, передавая подписчикам объект data.
unsubscribe
unsubscribe([channel,] context)
Отменяет подписку на канал channel в контексте объекта context, в котором выполнялся коллбэк. Если в качестве channel передан null, то данный метод отменяет все подписки объекта.
register
register(viewID/serviceID, fabric)
Регистрирует в ядре модуль с именем viewID/serviceID и конструктором fabric. Если модуль зарегистрирован автоматически, этот метод не требуется (см. объявление модуля).
registerAll
registerAll(arrayOfViews)
Вызывает register для каждого модуля из перечисленных в массиве arrayOfViews. Пример массива:
views = [ {"view.start_page": view.StartPage}, {"view.second_page": view.SecondPage}, ],
startViewOrService
startViewOrService(viewID, [extras])
Создает экземпляр модуля с именем viewID; использует объект extras в коллбэке самого модуля onNewExtras.
startPlugin
startPlugin(pluginID)
Создает экземпляр плагина с именем pluginID из списка зарегестрированных плагинов ядра plugins
stop
stop(viewID, callback, context)
Останавливает работу модуля с именем viewID, после чего выполняет callback в контексте объекта context.
getView
getView(viewID, [extras])
Возращает экземпляр модуля с именем viewID, передавая ему объект extras (см. onNewExtras). Если экземпляр не создан, инстанциирует его.
getStartedView
getStartedView()
Возвращает массив всех инстанциированных модулей, при этом их видимость на экране не обязательна.
Свойства ядра
Настройки ядра задаются объектом свойств, например:
coreOptions = { plugins: [ {"plugin.navigator": plugin.navigation}, {"plugin.fastclick": plugin.fastClick}, {"plugin.router": plugin.router} ], defaultBackstack: false, backstackType: 'native', defaultAnimation: 'slide', animationTimeout: 3000, debug: false }; //initialize core by new application object core.initialize(application, coreOptions);
plugins
... plugins: [ {"plugin.navigator": plugin.navigation}, {"plugin.fastclick": plugin.fastClick}, {"plugin.router": plugin.router} ], ...
Массив подключаемых плагинов. На данный момент плагинами реализованы роутер, layout manager и кроссбраузерные события для touch-устройств ("tap", "swipe" и др.). Подробнее об этом в разделе plugins.
defaultBackstack
... // no backstack defaultBackstack: false, // backstack enabled defaultBackstack: true, ...
Бэкстек по умолчанию для всех транзакций. При установке false история изменений содержимого экрана(смены расположения views) запоминаться не будет. Чтобы внести в историю отдельную транзакцию нужно использовать опцию backstack: true при использовании
navigation.
Подробнее о бэкстеке в разделе Backstack.
backstackType
... //backstack с использованием history API backstackType: 'native' //backstack с использованием hash-ссылок для history API backstackType: 'hashbang' //внутренняя реализация backstack backstackType: 'custom' ...
Тип бэкстека для всех транзакций. Если значение этого свойства не определено, тип бэкстека будет выбран автоматически ('native' либо 'hashbang', в зависимости от браузера). Подробнее о типах бэкстека в разделе Backstack.
defaultAnimation
... defaultAnimation: 'slide', ...
Анимация смены модулей по умолчанию. Может принимать значения 'slide' (сдвиг), 'fade' (затухание) и 'none' (мгновенное замещение). Подробнее - анимация модулей.
Плагины ядра
Плагины реализуют дополнительный функционал ядра, необходимый для работы системы и не зависящий от конкретной реализации логики приложения. Если нужно реализовать функциональный модуль без визуального представления в конкретном приложении, рекомендуется использовать сервисы.
Navigation
Плагин navigation обрабатывает все сообщения с корневым узлом 'navigation.' и занимается управлением views (отображение, скрытие, обеспечение бэкстека и нотификация об изменении состояния).
show
... var options = { container_id: '#screen', content: "view.start_page", animation: "none", backstack: false, callback: null, context: null, extras: null } this.publish('navigation.show', options); ...
Отображает указанный в options.content модуль в контейнере options.container_id (css-селектор).
Параметры options:
- content - зарегистрированное имя модуля (viewID), который будет отображен;
- container_id - селектор элемента-контейнера для отображения модуля;
- animation - анимация , используемая при смене view в указанном контейнере;
- backstack - сохранить историю изменения положения модулей (true или false)
- callback - функция, которая будет выполнена по завершению отображения модуля content
- context - контекст выполнения для callback
- extras - см. OnNewExtras
back
... this.publish('navigation.back', options); ...
Аналогичен navigation.show. При этом по умолчанию используется обратная анимация.
dialog.show
... this.publish('navigation.dialog.show', options); ...
Аналогичен navigation.show, но показывает модуль как модальное окно. Может быть закрыт при помощи dialog.close
toast.show
... options = { content: "view.toast", gravity: 'left', }; ... this.publish('navigation.toast.show', options); ...
Показывает модуль как оповещение, закрывающееся автоматически (по умолчанию - через 3 секунды) или по клику.
Параметр options.gravity - положение, принимает значения center (в центре экрана), left (прижат к левому краю), right(прижат к правому краю), top (низ экрана) и bottom (верх).
popup.show
... options = { content: "view.popup", target: document.getElementById(targetID), width: 180, height: 200, gravity: 'right', }; ... this.publish('navigation.popup.show', options); ...
Показывает модуль как неблокирующее всплывающее окно, которое может закрыватся автоматически по клику вне его.
Свойства options:
- target: элемент, относительно которого позиционируется popup;
- gravity: положение относительно target; принимает значения none (направление выбирается автоматически), center (в центре экрана), left, right, top и bottom
Fastclick
Плагин ядра, реализующий кроссбраузерные события «swipe», «tap», «tapdown», «tapup», «tapmove», «tapcancel»
Router
Используется ядром для навигации, реализует backstack.
backstack
options = { ... backstack: true, ... }
Компонент плагина router позволяющий динамически запоминать расположение (т.е. layout) views на экране для
конкретной сессии, используя history API браузера либо внутреннюю реализацию,
и таким образом возвращаться к предыдущим расположениям модулей. backstack не является
аналогом Routers в Backbone.js или Angular.js.
В плагине реализованы три типа бэкстека (backstackType):
- native - бэкстек с использованием history API браузера;
- hashbang - бэкстек с использованием генерации hash-ссылки (для браузеров, не поддерживающих history.pushState);
- custom - внутренняя реализация (без использования history API).
Если параметр defaultBackstack в настройках Core установлен в false,
то для использования бэкстека достаточно указать параметр backstack: true
в запросе на смену views; таким образом, следующее
расположение views на экране сохранится.
Перемещение назад по стеку осуществляется:
- кнопками браузера "Back" и "Forward", либо вызовом history.back() (для типов бэкстека 'native' и 'hashbang');
- публикацией сообщения 'router.back' (для всех типов)
... history.back(); // backstackType: 'native' или 'hashbang' ... this.publish('router.back', null); // любой backstackType ...
Router подписан на следующие сообщения:
- 'router.clear' - при получении сообщения удаляет всю историю навигации данной сессии (опустошает бэкстек);
- 'router.back' - при получении сообщения вызывает возврат на один шаг назад по бэкстеку;
- 'router.beginTransition' - сообщение публикуется плагином navigation перед началом анимации (transition) смены view;
- 'router.endTransition' - сообщение публикуется плагином navigation, когда анимация смены view завершена. При этом происходит добавление URL предыдущего положения модулей в history (бэкстек-типы 'native', 'hashbang'), либо внутренний стек ('custom')
... this.publish('router.clear', null); //обнуление существующего backstack ... this.publish('router.back', null); //возврат назад по backstack ...
Router публикует следующие сообщения:
- 'backstack.pop' - сообщение публикуется в момент, когда происходит возврат назад по бэкстеку;
- 'backstack.empty' - сообщение публикуется в момент опустошения бэкстека.
Пример использования backstack:
RAD.view("view.screen_1", RAD.Blanks.View.extend({ url: 'source/views/screen_1/screen_1.html', events: { 'tap button.next-scr': 'open' } open: function () { this.publish('navigation.show', { content: 'view.screen_2', container_id: '#content', backstack: true //по завершению смены view будет сохранен URL соответствующий их текущему расположению }); } })); RAD.view("view.screen_2", RAD.Blanks.View.extend({ url: 'source/views/screen_2/screen_2.html', events: { 'tap button.next-scr': 'open' } open: function () { this.publish('navigation.show', { content: 'view.screen_3', container_id: '#content', backstack: true //по завершению смены view будет сохранен URL соответствующий их текущему расположению }); } })); RAD.view("view.top_widget", RAD.Blanks.View.extend({ className: 'block', url: 'source/views/top_widget/top_widget.html', events: { 'tap button.go-back': 'goBack' }, goBack: function () { "use strict"; this.publish('router.back', null); //возврат к последнему сохраненному расположению модулей } }));
Объявление модуля
Пример файла модуля start_page.js, объявление view через шаблон 'namespace':
RAD.views.StartPage = RAD.Blanks.View.extend({ url: 'source/views/start_page.html' })
RAD.view("view.start_page", RAD.Blanks.View.extend({ url: 'source/views/start_page.html' }));
Methods
subscribe
... this.subscribe(channel, function, context); ...
Подписывает данный экземпляр модуля на указанный канал, является просто прямой ссылкой на метод ядра subscribe.
unsubscribe
... this.unsubscribe([channel,] context); ...
Отписывает данный экземпляр модуля от указанного канала; является просто прямой ссылкой на метод ядра unsubscribe.
publish
... this.publish(channel, data); ...
Публикует сообщение содержащее данные data, в указанный канал channel; является прямой ссылкой на метод ядра publish.
$
... this.$('css_selector'); ...
Ищет указанный CSS-селектор в данном модуле и оборачивает найденый елемент в JQuery.
Аналог:
this.$el.find('css_selector');
getChildren
... this.getChildren(); ...
Возвращает массив дочерних модулей, заданных при объявлении в children или находящихся в данный момент в этом view.
bindModel
... this.bindModel(model); ...
Устанавливает модель для модуля. При этом будет вызван render() для перерисовки модуля. Модуль будет автоматически подписан на события модели.
changeModel
... this.changeModel(model); ...
Заменяет модель модуля на переданную в model. При этом будет вызван render() для перерисовки модуля.
unbindModel
... this.unbindModel(forceRender); ...
Удаляет модель модуля. Если параметр равен true, то при этом будет вызван render() для перерисовки модуля.
Callbacks
Коллбэки описываются при объявлении view, например:
RAD.view("view.start_page", RAD.Blanks.View.extend({ url: 'source/views/start_page.html', onEndRender: function () { "use strict"; console.log('page rendered!'); } }));
Представляют собой функции, вызываемые при событиях жизненого цикла view:
// выполняется во время создания экземпляра onInitialize: function () {}, // выполняется при получении view данных через navigator onNewExtras: function () {}, // выполняется при получении сообщения на канал совпадающий с viewID onReceiveMsg: function () {}, // выполняется перед началом шаблонизации(создания html view) onStartRender: function () {}, // выполняется после создания html содержимого из шаблона, // iScroll в наследниках RAD.Blanks.ScrollableView при первом отображении еще не существует onEndRender: function () {}, // выполняется перед началом анимации присоеденения view, // iScroll в наследниках RAD.Blanks.ScrollableView уже существует onStartAttach: function () {}, // выполняется после окончания анимации присоеденения view onEndAttach: function () {}, // выполняется после удаления view из DOM onEndDetach: function () {}, // выполняется при уничтожении модуля onDestroy: function () {}
onInitialize
... onInitialize: function(){ }; ...
Выполняется первым из коллбэков в конце конструктора при создании экземпляра view. Это последний момент, когда можно напрямую задать модулю модель. В дальнейшем необходимо использовать такие методы view, как bindModel и unbindModel.
onStartRender
... onStartRender: function(){ }; ...
Выполняется перед render() модуля. На этом этапе HTML-представления модуля еще нет.
onEndRender
... onEndRender: function(){ }; ...
Выполняется в render() модуля, когда HTML-представление сгенерировано из шаблона. Обработка всего, что связано с HTML(например, вставка дочерних модулей или добавление классов), должна происходить в этом методе.
onNewExtras
... var options = { ... extras: { hello : 'world' } } this.publish('navigation.show', options); ... onNewExtras: function(extras){ console.log(extras.hello); }; ...
Выполняется при передачи через навигатор новых extras. Идеально подходит для передачи новой модели или параметров отображаемого модуля. Принимает параметр extras.
onStartAttach
... onStartAttach: function(channel, options){ }; ...
Выполняется перед отображением модуля и перед началом анимации (даже если ее нет). В качестве параметров принимает канал, по которому пришло сообщение, и данные от плагина navigation.
onEndAttach
... onEndAttach: function(channel, options){ }; ...
Выполняется после окончания отображениея модуля и после окончания анимации (даже если ее нет). В качестве параметров принимает канал, по которому пришло сообщение, и данные от плагина navigation.
onEndDetach
... onEndDetach: function(channel, options){ }; ...
Выполняется после окончательного отсоеденения модуля из текущего DOM. В качестве параметров принимает канал, по которому пришло сообщение, и данные от плагина navigation.
onDestroy
... onDestroy: function(){ }; ...
Вызывается перед уничтожением экземпляра модуля (деструктор). Нет входных параметров.
onReceiveMsg
... onReceiveMsg: function(msg, data){ }; ...
Модуль при создании автоматически подписывается на сообщения, начинающиеся с "view." и его имени, например, модуль myModule подпишется как на сообщения "view.myModule", так и на "view.myModule.doSomeAction".
Параметры:
msg - канал сообщения, строка;
data - объект переданных данных (см. публикацию сообщений publish).
loader.done
... onNewExtras: function (extras) { var self = this; self.loader.done(function () { self.$("#options").html(extras.data); }); }, ...
Это не совсем коллбэк-метод. Каждый модуль имеет deferred-объект loader, которому возможно передать функцию, которая выполнится после загрузки HTML-представления модуля. В случае, если HTML или HTML-шаблон были загружены, функция выполнится сразу. Например, можно использовать этот метод тогда, когда переданные через extras данные необходимо вставить в готовый HTML.
Свойства View
Модули унаследованы от backbone.view, здесь описаны только некоторые свойства.
url
... url: 'source/views/inner/third_widget/third_widget.html' ...
Ссылка на файл HTML, используемый модулем в качестве шаблона.
tagName и ClassName
... tagName: 'li', className: 'my_list_item' ...
Определяют, каким элементом будет представлен контейнер модуля и его класс(ы).
children
... children:[ { container_id: '.sidebar', content: 'view.sidebar_menu', }, { container_id: '.content', content: 'view.default_content', } ] ...
Массив дочерних модулей, которые будут загружены и показаны вместе с родительским. Атрибуты аналогичны 'navigation.show'
events
... events:{ 'focus .search-by-name': 'showAutocomplete', 'click .my_button':'buttonAction' }, buttonAction: function(e){ console.log(e.currentTarget + ' clicked'); }, showAutocomplete: function(){} ...
Подписка элементов модуля на события.
model
... //декларативный способ model: RAD.models.noteList; ... //задание модели при инициализации ... onInitialize: function () { "use strict"; this.model = RAD.models.noteList; this.model.add([ {title:'test note 1', 'description':'test note description 1'}, {title:'test note 2', 'description':'test note description 2'}, {title:'test note 3', 'description':'test note description 3'} ]); }, ...
Задает модель данных для модуля. Подробнее о моделях в разделе model. View автоматически подписывается на события модели.
Toast
Модуль для оповещений, автоматически закрывающийся через определенное время (по умолчанию - 3 секунды). См. navigation.toast.show.
RAD.view("view.CompleteToast", RAD.Blanks.Toast.extend({ //шаблон url: 'source/views/start_page.html', //время показа showTime: 5000 }));
Popup
Модуль реализует самопозиционирующийся попап. Способы позиционирования задаются в опциях navigation.popup.show.
RAD.view("view.PopupOverview", RAD.Blanks.Popup.extend({ url: 'source/views/popup_dashboard_overview/popup_dashboard_overview.html', onInitialize: function (){ var Model = Backbone.Model.extend(); this.model = new Model(); }, onNewExtras: function (extras) { 'use strict'; this.model.set({msg: extras}); }, // будет ли popup закрыватся по клику вне модуля outSideClose: true, // будет ли уничтожатся инстанс модуля при закрытиии(по умолчанию - уничтожается) // если надо не уничтожать - прописать следующую строку onCloseDestroy: false });
Scrollable view
Модуль со скроллящимся контентом, использует доработанную версию iScroll-lite.
RAD.view("view.start_page", RAD.Blanks.ScrollableView.extend({ url: 'source/views/start_page.html' }));
Для правильной работы скролла при изменении размера скроллящегося контента, для его контейнера необходим установленный атрибут data-template.
Animation
Вид анимации, используемой при отображении модулей. Возможны следующие значения:
- 'slide' или 'slide-in'(абсолютно аналогично) - новый модуль выезжает справа, старый заезжает влево;
- 'slide-out' - анимация противоположная 'slide-in';
- 'fade' - альфа анимация; старый модуль затухает, а новый проявляется;
- 'none' - без анимации, новый модуль просто вставляется вместо старого;
- не определено - выставляется по умолчанию 'slide'.
/* Возможно использование кастомной анимации, */ /* для этого необходимо описать в CSS анимацию, подобную следующей */ /* суфиксы -in и -out говорят о направлении */ .new-page.fade-in, .new-page.fade-out { opacity: 0; -webkit-transition: opacity 350ms ease; -moz-transition: opacity 350ms ease; -ms-transition: opacity 350ms ease; -o-transition: opacity 350ms ease; transition: opacity 350ms ease; } .old-page.fade-in, .old-page.fade-out { opacity: 1; -webkit-transition: opacity 175ms 175ms ease; -moz-transition: opacity 175ms 175ms ease; -ms-transition: opacity 175ms 175ms ease; -o-transition: opacity 175ms 175ms ease; transition: opacity 175ms 175ms ease; } .animate > .new-page.fade-in, .animate > .new-page.fade-out { opacity: 1; } .animate > .old-page.fade-in, .animate > .old-page.fade-out { opacity: 0; }
Шаблоны
Шаблоны полезны при рендеринге объемных и сложных частей HTML-разметки из JSON-данных. Для осуществления шаблонизации используется метод из библиотеки Underscore.js, который компилирует шаблоны в функции, которые могут быть вызваны для рендеринга этого шаблона. Путь к HTML-шаблону передается в качестве свойства url при обьявлении модуля View. При рендеринге HTML-представления модуля в функцию-шаблонизатор передается view.model этого модуля, преобразованная в JSON-объект.
Пример разметки с шаблонами:
<div class="scroll-view-body"> <div class="block"> <div class="block-title"> <h2>All Action Items</h2> </div> <ul class="list-items-view"> {{# _(model).each(function(action) { }} <li > <span class="priority-indicator {{ action.priority }}"></span> <div class="list-item-row row-title"> <div class="info">{{ action.title }}</div> <div class="details">Due: {{ action.due }}</div> </div> <div class="list-item-row"> <div class="info">Patient: {{ action.patient }} (DOB: {{ action.dob }} )</div> <div class="details">CM Program: {{ action.cm_program }}</div> </div> </li> {{# }); }} </ul> </div> </div>
Синтаксис внутри шаблона:
- {{ ... }} - для интерполяции переменных;
- {{# ... }} - для выполнения вычислений (JavaScript-код внутри шаблона);
- {{{ ... }}} - для экранирования спец-символов (HTML-escaped);
- model - преобразованная в JSON-объект view.model
Для построения HTML из большого массива данных удобно использовать методы библиотеки Underscore, такие как each(), sortBy(), filter() и т.д.
Пример использования методов Underscore.js в шаблоне
RAD.view("view.persons_list", RAD.Blanks.View.extend({ url: "source/views/persons_list.html", onInitialize: function () { this.model = RAD.model('persons', Backbone.Collection, true); this.model.add([ {"firstName": "John", "lastName": "Doe"}, {"firstName": "Homer", "lastName": "Simpson"}, ... ... {"firstName": "Fox", "lastName": "Mulder"} ]); } }));
<!-- Шаблон persons_list.html: --> <ul class="persons_list"> {{# _(model).each(function (person) { }} <li>{{ person.firstName }} <b>{{ person.lastName }}</b></li> {{# }); }} </ul> <!-- После шаблонизации: --> <ul class="persons_list"> <li>John <b>Doe</b></li> <li>Homer <b>Simpson</b></li> ... ... <li>Fox <b>Mulder</b></li> </ul>
Для того, чтобы при изменении model была перерисована лишь часть модуля, контейнеру, содержащему шаблон, необходимо присвоить атрибут data-template.
Пример использования атрибута data-template:
<div id="my_module"> <!-- содержимое <div> не будет перерисовано (радиокнопка не будет сброшена в положение по умолчанию) --> <div class="controls"> <p>Sort by:</p> <label><input type="radio" name="sort_by" value="first_name" />First Name</label> <label><input type="radio" name="sort_by" value="last_name" checked />Last Name</label> </div> <!-- содержимое <ul> будет перерисовано --> <ul class="persons_list" data-template> {{# _(model).each(function (person) { }} <li>{{ person.firstName }} <b>{{ person.lastName }}</b></li> {{# }); }} </ul> </div>
Model
В качестве модели данных используется backbone.model.
//создание модели RAD.model('name', <backbone.model>, [instantiate]); //получение модели (вернет undefined, если модели нет) RAD.model('name');
Для работы с моделями вызывается метод RAD.model(...), где используются следующие параметры:
-
name - имя модели, под которым в RAD.models будет создана модель. Может быть задано в виде:
<namespace>.<subnamespace>.name - backbone.model - модель backbone (пример ниже);
- instantiate - параметр создания модели. true (или не передан) - создать экзепляр, false - сохранить как конструктор.
RAD.model('note', Backbone.Model.extend({defaults: { title: "-", description: "-" } }), false);
Создание модели в модуле:
... onInitialize: function () { "use strict"; var md = RAD.model('note'); this.model = new md(); this.model.set({title:'test note 1', 'description':'test note description 1'}) }, ...
Использование модели в шаблоне:
... <div class="my page"> <h3>Use models, Luke!</h3> <div id="title">{{ model.title }}</div> <div id="description">{{ model.description }}</div> </div> ...
Модель уровня приложения
Если необходимо, чтобы модель была доступна нескольким модулям, можно создать экземпляр модели на уровне приложения, передав последним параметром true.
RAD.model('message', Backbone.Model.extend({ defaults: { title: "-", description: "-" } }), true);
... RAD.model('message').set({title: 'new note', description: 'lorem ipsum dolor'}); ...
Service
Сервисы не имеют визуального представления и могут быть использованы для обработки внутренней логики приложения. Имеют некоторые callback-методы аналогичные view; также могут иметь произвольные свойства.
Объявление
Сервисы объявляются аналогично модулям и тоже инстанциируются при получении первого сообщения.
RAD.service("service.my_service", RAD.Blanks.Service.extend({ onReceiveMsg: function (channel, data) { "use strict"; var backway = data.split("").reverse().join(""); this.publish('view.widget2', backway); } }));
Callbacks
Коллбэки сервисов аналогичны view callbacks, но из-за отсутствия визуального представления применимы не все.
onInitialize
... onInitialize: function(){ }; ...
Выполняется первым из коллбэков в конце конструктора при создании экземпляра service. Это последний момент, когда можно напрямую задать модулю модель. В дальнейшем необходимо использовать такие методы view, как bindModel и unbindModel.
onDestroy
... onDestroy: function(){ }; ...
Вызывается перед уничтожением экземпляра сервиса (деструктор). Нет входных параметров.
onReceiveMsg
... onReceiveMsg: function(msg, data){ }; ...
Сервис при создании автоматически подписывается на сообщения, начинающиеся с "service." и его имени. Аналогично
module.onReceiveMsg
Параметры:
msg - канал сообщения, строка;
data - объект переданных данных (см. публикацию сообщений publish).
Utils
Набор полезных методов для использования в модулях. Расположенных в отдельном файле utils.js
removeMultipleSpaces
RAD.utils.removeMultipleSpaces(str)
Заменяет в строке str множественные (более одного) пробелы одним символом. Например, RAD.utils.removeMultipleSpaces('one, two, three, four') вернет 'one, two, three, four'.
getCoords
RAD.utils.getCoords(elem, [parent])
Возвращает top и left координаты html-элемента elem (в px) относительно родительского
элемента parent. Если parent не передан - вернет координаты относительно окна браузера.
Пример:
... <div id="foo" style="padding: 10px"> <div id="bar">Lorem Ipsum</div> </div> ...
var elem = $el.find('#bar'), parent = $el.find('#foo'); RAD.utils.getCoords(elem, parent); //вернет {top: 10; left: 10}
dispatchResizeEvent
RAD.utils.dispatchResizeEvent(targetEl)
Этот метод будет полезен, когда необходимо принудительно обновить размеры скроллящегося контента внутри targetEl (scroll-view элемента, см. ScrollableView)
Base64
RAD.utils.Base64.encode(input); //кодирование ... RAD.utils.Base64.decode(input); //декодирование
Предоставляет методы для кодирования encode и декодирования данных input по системе Base64 для
дальнейшего хранения либо передачи по сети.
Пример:
RAD.utils.Base64.encode('Hello, World!'); //вернет "SGVsbG8sIFdvcmxkIQ==" ... RAD.utils.Base64.decode('SGVsbG8sIEJhc2U2NCE='); //вернет "Hello, Base64!"
QueryFactory
var factory = new RAD.utils.QueryFactory();
Конструктор для Promise-объектов
1) query = factory.createQuery ({options}) - в качестве опций указывается функции в случае успеха, в случае ошибки, и контекст. В каком случае выполняется функция ошибки: когда прерывается очередь выполнения или в каком-то другом случае?
2) query.then(fn) - добавляет в конец очереди выполнения функцию. в нее обязательно должен быть передан prom? prom - это наш query?
3) prom.next(data) - вызывает следующую функцию в очереди. аргументом являются какие-то данные которые хотим передать в следующую функцию?
4) судя по utils.js и по примеру query.when(fn) тоже добавляет в очередь выполнения функции но когда они выполняются?
5) prom.success(data) - это обязательно должно быть в последней функции в очереди или может быть вставлено в любую функцию в очереди? если нужно чтобы сработала error нужно сделать prom.error()?
serializeFormToObject
RAD.utils.serializeFormToObject(formSelector)
Вернет поля формы найденной по селектору formSelector преобразованные в объект
Пример:
<form name="poll" id="poll"> <p>Please, introduce yourself</p> <label for="firstname">First Name</label> <input id="firstname" name="firstname" type="text" /><br> <label for="lastname">Last Name</label> <input id="lastname" name="lastname" type="text" /><br> <p>Your age between:</p> <label><input name="age" value="6-17" type="radio" />6-17</label> <label><input name="age" value="18-29" type="radio" checked />18-29</label> <label><input name="age" value="30-41" type="radio" />30-41</label> <label><input name="age" value="42-99" type="radio" />42 and older</label><br><br> <label>I have read and accept the terms of <a href="#">privacy policy</a> <input name="privacy_pol" value="agree" type="checkbox" /> </label> </form>
RAD.utils.serializeFormToObject('#poll'); // вернет // { // firstname: "", // lastname: "", // age: ["6-17", "18-29", "30-41", "42-99"], // privacy_pol: "agree" // }
serializeFormToString
RAD.utils.serializeFormToString(formSelector)
Вернет поля формы найденной по селектору formSelector преобразованные в строку
Так, например, для формы в примере выше:
RAD.utils.serializeFormToString('#poll'); // вернет "{age:['6-17', '18-29', '30-41', '42-99'],firstname:'',lastname:'',privacy_pol:'agree'}"
Namespace
Фреймворк позволяет использовать шаблон 'namespace' (пространства имен) для структурированного хранения собственных конструкторов models, views, services и т.д., или наследования от существующих.
RAD.namespace(destination, [obj]);
- destination - местонахождение namespace
- obj - объект, который будет определен в этом namespace
//Создание объекта authServer в пространстве имен RAD.network.nodes RAD.namespace('network.nodes.authServer', {name: 'AuthServer', baseUrl: 'http://192.168.1.1/'}); //Создаст в пространстве RAD.utils метод getRndInt RAD.namespace('RAD.utils.getRndInt', function (min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; }); RAD.namespace('RAD.models.toDoList', Backbone.Collection.extend({ comparator: function (task) { return task.get("name"); } })); //Пример наследования view от RAD.Blanks.ScrollableView и последующего его объявления. RAD.namespace('RAD.views.ListView', RAD.Blanks.ScrollableView.extend({ model: new RAD.models.toDoList })); RAD.view('view.todo_list', RAD.views.ListView);
Фреймворком предоставляются для использования готовые namespace:
- RAD.Class - для собственных классов;
- RAD.models - для моделей и коллекций;
- RAD.views - для конструкторов View;
- RAD.services - для сервисов;
- RAD.plugins - для плагинов;
- RAD.utils - для полезных методов-утилит.
FAQ
Часто задаваемые вопросы.
-
Как узнать какие view видны на экране?
querySelectorAll(«[view]»);
-
Как получить ссылку на HTML-елемент-контейнер, в котором находится view?
querySelector(«[view='viewID']»);
-
Как получить список всех view, экземпляры которых созданы?
RAD.core.getStartedViews();
К счастью (или к сожалению), у нас пока не было не одного реально необходимого случая для вызова данного метода. -
Получение экземпляра любого модуля приложения.В любой части приложения возможно получить непосредственную ссылку на любой зарегистрированный модуль приложения(view или сервис) через прямую ссылку на ядро:
RAD.core.getView(viewID, extras);
RAD.core.getService(viewID);
Учтите, что архитектура приложения на RAD.js, рассчитана на слабую связанность модулей и динамическое создание/уничтожение частей приложения, поэтому использовать данный механизм не рекомендуется.
Связано это с тем, что данные методы вернут вам ссылку на экземпляр уже существующего модуля с указанным ID, или же создадут новый. Если вы сохраните эту ссылку, например, в атрибуте вашего view, какая-нибудь другая часть вашего приложения (опять же, через методы ядра) может удалить экземпляр модуля, а потом создать его экземпляр уже с другими данными или моделью.
Итогом будет наличие сильной связи, наличие ссылки на модуль, который уже уничтожен и нигде больше не используется. В качестве рекомендации можно предложить использовать методы ядра только в объекте приложения. В этом случае вся работа по созданию и уничтожению модулей будет сосредоточена в одном месте, и найти логическую ошибку будет намного легче. -
Модули являются Backbone.View?Нет, но они созданы на основе Backbone.View и имеют почти те же задачи, основная из которых - отображение модуля на странице
-
Что такое канал (channel)? У каждого модуля свой channel? Как происходит subscribe и publish в определенный channel? В канал публикуется просто некое сообщение, и модуль, который слушает канал в ожидании этого сообщения должен на него среагировать?Канал является совокупностью публикатора, модуля подписчика и медиатора. Сообщениями, которыми обмениваются эти компоненты, являются события (“команды”, “намерения”).
-
Что такое extras?Дополнительные данные, которые передаются во view при публикации сообщения через navigator.
-
На какие сообщения подписываются модули при регистрации?На события, имя которых начинается с "view." + имя модуля. При публикации такого сообщения вызывается onReceiveMsg этого модуля.
-
Что такое backstack? Как именно работает и какие свойства нужно задать для его правильной работы?Это компонент плагина router, позволяющий динамически запоминать расположение (т.е. layout) views на экране для конкретной сессии, используя history API браузера либо внутреннюю реализацию - и таким образом возвращаться к предыдущим расположениям модулей.
Для использования бэкстека достаточно указать параметр backstack: true в запросе на смену views. Более подробная информация в разделе backstack -
Будет ли работать анимация дочерних модулей, если задать её таким образом? Например:
RAD.views.ParentWidget = RAD.Blanks.View.extend({ url: 'source/views/parent_widget/parent_widget.html', children: [ { container_id: '.content', content: "view.inner_first_widget", animation: 'slide' }, { container_id: '.top', content: "view.inner_third_widget", animation: 'fade' } ] });
Да, но использовать не рекомендуется - будет визуально некрасиво. -
Что происходит с дочерними модулями ("children") во время рендеринга родительского модуля?Во время самого первого рендеринга родительского модуля происходит следующее:
- Выполняется коллбэк onStartRender() (если определен);
- Происходит рендер модуля;
- Происходит присоединение (attach) дочерних модулей;
- Выполняется коллбэк onEndRender() (если определен).
Во время повторного рендеринга в порядок действий добавляется предварительное отсоединение дочерних модулей:- Выполняется коллбэк onStartRender() (если определен);
- Происходит отсоединение (detach) дочерних модулей;
- Происходит рендер модуля;
- Происходит присоединение (attach) дочерних модулей;
- Выполняется коллбэк onEndRender() (если определен).
Таким образом, заново рендерится только родительский модуль, а "children" - нет. -
Можно ли "прикрепить" iScroll к какому либо дочернему HTML-элементу модуля при отображении этого модуля?Да, контейнеру для iScroll указать класс "scroll-view".
-
Какие существуют события iScroll?См. события в документации iScroll4.