Иногда попадаются интересные задачи, которые сложно написать с первого раза. Одной из таких задач оказались каскадные списки и легким комментарием я прикинул, что на Backbone у меня получится написать короче.
Короче не получилось… Отчасти из-за того, что я усложнил задачу до бесконечно выпадающих списков и из-за небольшой многословности Backbone.
Рисуем две окружности...
Немного подумав в голове появляется примерно такая блок-схема:
При проектировании интерфейсов я всегда начинаю с самых мелкий деталей, в случае списка — это option и представление SelectItemView
, которое его рендерит. Далее элементы образуют список, а списки образуют то, что нам нужно — базовое представление BaseView
.
Задача стоит сделать бесконечно сложенные списки и для её решения можно применить что-то вроде связного списка для HTML:
<script type="text/template" id="base-tempate">
<div class="primary"><!-- SelectView --></div>
<div class="secondary"><!-- вложенный BaseView, если нужен --></div>
</script>
В контейнер .secondary
будут рекурсивно вкладываться дочерние списки, а при изменении активного элемента .primary
будет происходить очистка .secondary
и рендер нового списка.
… И дорисовываем остальную сову
Спустя почти два часа я получил примерно такую демку. Код довольно простой и понятный, я не нашел того, что можно там долго объяснять.
Интересные моменты
1. Чтобы не вспоминать у каких элементов есть вложенный список, в конструкторе модели ItemModel
происходит добавление значка ">" к метке:
initialize: function(){
if(this.get('items').length > 0){
this.set('label', this.get('label') + ' >');
}
2. После изменения выбранного элемента нужно найти модель, которая за него отвечает. Для решения задачи пришлось сделать массив itemViews
представлений элемента, которые были отрендерины:
changeItem: function(){
u.each(this.itemViews, function(view){
if(view.el === this.el.options[this.el.selectedIndex]){
this.collection.selectedModel = view.model;
this.collection.trigger('changeSelectedItem');
}
}, this);
},
3. Вышеприведенный код посылает сигнал changeSelectedItem
, который ловит BaseView
и пытается отрендерить вторичный список, если есть нужная коллекция:
renderSecondary: function(){
var collection = this.collection.selectedModel.itemsCollection;
var container = this.$el.find('.secondary');
container.empty();
if(collection)
(new BaseView({ collection: collection })).render().$el.appendTo(container);
}
Пояснения по коду
Весь код находиться в такой обёртке:
(function(j, b, u){
// j - jQuery
// b - Backbone
// u - Underscope
})(jQuery.noConflict(), Backbone.noConflict(), _.noConflict());
Обычно я пишу обертку чуть посложней, но в данном случае весь код находится в одном файле и этого достаточно. Заранее не прошу не критиковать сокращения, мне так удобней и читается проще, чем всякие доллары и нижние прочерки.
Заключение
Каждый раз, когда работаю с Backbone, радует аккуратность полученного кода. Хотя, возможно, это лишь для меня он получается аккуратный?
Автор: sdevalex