Pull to refresh

Отображение зависимых данных на примере каскадных выпадающих списков

JavaScript *
Иногда попадаются интересные задачи, которые сложно написать с первого раза. Одной из таких задач оказались каскадные списки и легким комментарием я прикинул, что на 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, радует аккуратность полученного кода. Хотя, возможно, это лишь для меня он получается аккуратный?
Tags:
Hubs:
Total votes 12: ↑7 and ↓5 +2
Views 3.1K
Comments Comments 4