- PVSM.RU - https://www.pvsm.ru -
В клиентских приложениях очень часто возникает необходимость как-то визуализировать процесс загрузки данных с сервера. В этой статье я опишу способ, позволяющий добиться такого поведения за счёт повторно используемой области Marionette.Region в MarionetteJS.
Сразу скажу, что мой подход во многом основывается на подходе автора скринкастов на www.backbonerails.com [1]. Это очень хорошая и полезная серия скринкастов не только (и не столько) с точки зрения того, что обсуждается здесь, но и в целом для изучения MarionetteJS.
Итак, нашей целью является разработка повторно используемого компонента для MarionetteJS, занимающегося визуализацией процесса загрузки. Как будет показано дальше, в случае Backbone/Marionette события начала и окончания загрузки можно считать частным случаем изменения состояния модели, поэтому назначение этого компонента можно сформулировать более абстрактно как визуализацию изменения состояния модели.
Как всем хорошо известно, MarionetteJS является надстройкой над BackboneJS. И я бы хотел начать с того, как Backbone может помочь с решением этой задачи. Допустим, у нас есть модель (или коллекция) и представление, которое её отображает. На уровне BackboneJS поставленную задачу мы могли бы решить следующим образом:

Рисунок 1 — События модели при загрузке данных
То есть нехитрая идея состоит в том, что представление обозревает модель и использует события request и sync, генерируемые Backbone.Model, для перехода к «загрузочному» (например, рисует какую-нибудь анимацию загрузки) или к «синхронизированному» состоянию (убирает анимацию загрузки). Для краткости, я буду называть способность реагировать на события изменения состояния модели такие как request, sync, error и т.п. отзывчивостью. Под изменением состояния я имею в виду любые события, не связанные с изменением данных модели.
В общем, Backbone позволяет нам легко добиться нужного поведения, за счёт событий, генерируемых Backbone.Model (или Backbone.Collection), но есть проблемы с повторным использованием кода, который должен подписаться на события модели и обработать их. Собственно, с вопросом как реализовать отзывчивость проблем не возникает, главный вопрос где это можно сделать, чтобы использование этой реализации было наиболее ненавязчивым и удобным. Я не буду останавливаться на дальнейших рассуждениях, как можно было бы реализовать наш компонент на основе Backbone, потому что в любом случае лучше, чем на основе MarionetteJS, он не получится.
Когда я в первые столкнулся с задачей визуализации загрузки, я не думал, что по этому вопросу будет так мало готовых решений. Гугление находило несколько обсуждений, насчёт реализации атрибута Marionette.View.loadingView аналогично Marionette.CollectionView.emptyView. Позже появилось и какое-то готовое решение [2]. Но честно говоря, я считаю, что визуализация загрузки модели на уровне представления, непосредственно отображающего эту модель, не самая лучшая идея. В общем случае, способ визуализации загрузки зависит не от представления модели, а от того, кто его отображает. Т.е. если мы отображаем разные модели в одном и том же месте документа, то и визуализация загрузки должна выглядеть единообразно для всех них. Короче говоря, этот вариант нам не подходит.
Теперь пришло время рассказать о подходе, на основе которого возникла моя идея. Это подход описан в этом выпуске [3] уже упоминавшейся серии скринкастов. Если в двух словах, этот подход использует модель как источник событий и базовый абстрактный контроллер, определяющий способ отображения представлений. Вот в общих чертах этот подход:

Рисунок 2 – Диаграмма последовательности при использовании LoadingView+LoadingController
Вот диаграмма классов, которые участвуют в этом взаимодействии:

Рисунок 3 – Диаграмма классов при использовании LoadingView+LoadingController
Это первый стоящий подход из тех, что уже упоминались. Но мне он не подошёл по той причине, что он налагает некоторые требования на общую архитектуру приложения:
В то же время есть очень удачные решения, которые я позаимствовал:
Marionette.Region олицетворяет какую-то область экрана, в которой размещаются представления, и позволяет управлять временем жизни представления и отделять от представления способ его появления на экране. Если вы хотите показать в области представление, вам не нужно думать, что будет с тем представлением, которое уже помещено в область — оно будет удалено автоматически. Если вам нужно изменить способ появления представления на экране, вы можете просто унаследоваться от Marionette.Region и реализовать свою логику. Например, вы можете поменять способ появления, добавив анимацию, а можете изменить способ вставки представления в документ, оборачивая его какими-то своими элементами. Для примера, в этом выпуске [4] как раз описывается реализация собственной области, оборачивающей произвольное представление в диалоговое окно.
В моей реализации основная работа происходит в абстрактном классе ResponsiveRegion, являющимся наследником Marionette.Region. Конкретным классам остаётся только определить обработчики, меняющие меняется внешний вид области в зависимости от событий модели (и перечислить сами модели). Например, можно менять прозрачность, видимость элементов области, вставлять какой-нибудь оверлей с анимацией, — в общем, делать всё что угодно. Я не буду уделять много внимания оформительской части, а сосредоточусь на абстрактном классе. Вот как я реализовал отзывчивость на уровне области при помощи ResponsiveRegion:
List.Layout = Marionette.LayoutView.extend({
...
regions : {
someRegion : {
selector : '#region',
regionClass : Marionette.ResponsiveVisiblityRegion
}
}
...
});
<div class=”js-model-state-sync”>
Сюда помещается исходное представление
</div>
<div class=”js-model-state-request”>
Вид при загрузке модели
</div>
<div class=”js-model-state-error”>
Вид при ошибке загрузки модели
</div>
Диаграмма последовательностей выглядит так:

Рисунок 4 — Диаграмма последовательности при использовании ResponsiveRegion
А классов так:

Рисунок 5 — Диаграмма классов при использовании ResponsiveRegion
Чего мы добились, применив такой подход?
Все из рассмотренных подходов могут успешно применяться в зависимости от ситуации. Но я считаю для визуализации изменения состояния модели вообще и загрузки её данных в частности, вариант с Marionette.Region подходит лучше всего. Во-первых, это как раз тот компонент, который в Marionette отвечает за отображение представлений на экране. А во-вторых, необходимая логика достаточно проста, чтобы её можно было реализовать внутри одного компонента. На этом всё. Исходный код того, о чём здесь говорилось, и небольшой пример доступны здесь [5].
Автор: DriverEntry
Источник [8]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/javascript/85582
Ссылки в тексте:
[1] www.backbonerails.com: http://www.backbonerails.com
[2] решение: https://github.com/gogiel/marionette.loading
[3] этом выпуске: http://www.backbonerails.com/screencasts/loading-views
[4] этом выпуске: http://www.backbonerails.com/screencasts/building-dialogs-with-custom-regions
[5] здесь: https://github.com/DriverEntry/responsive-region
[6] Документация MarionetteJS: http://marionettejs.com/docs/current/
[7] Список событий BackboneJS: http://backbonejs.org/#Events-catalog
[8] Источник: http://habrahabr.ru/post/252909/
Нажмите здесь для печати.