Pull to refresh

Отображение зависимых данных, на примере использования двух ниспадающих списков, посредством knockout

JavaScript *
В этом посте я хотел бы показать основные возможности knockout по работе с данными в раскрывающихся списках (тег
). Как оказалось тут есть свои мелочи, на которые стоит обратить внимание при использовании, такого мощного инструмента как knockout. 

Добро пожаловать под кат!



Описание


Передо мной встала задача реализации показа зависимых (связных данных). Т.е. на основе выбранного типа организации, нужно было отобразить данные о суб типах организации в другом (зависимом) раскрывающемся списке.

Реализация (приводится упрощенная реализация)



Первоначальная реализация с применением биндинга по идентификатору
В ходе реализции были созданы следующие модели классов:

Описание моделей на javascript



Модель типа организации:

function OrgType(idv, namev, subTypesv){ var id = ko.observable(idv), name = ko.observable(namev), subTypes = ko.observableArray(subTypesv); return { id : id, name : name, subTypes : subTypes }; }

Модель суб типа организации:


function OrgSubType(idv, namev){
        var id = ko.observable(idv),
            name = ko.observable(namev);
        return {
            id : id,
            name : name            
        };         
    }


И основная (главная) модель для отображения на форме:



function OrgTypesModel(){
        var orgTypes = ko.observableArray([
            new OrgType(1,'tp1',
                        [new OrgSubType(1,'subT1'), 
                         new OrgSubType(2,'subT2'), 
                         new OrgSubType(3,'subT3')]),
            
            new OrgType(2,'tp2',
                        [new OrgSubType(4,'subT4'), 
                         new OrgSubType(5,'subT5'), 
                         new OrgSubType(6,'subT6'),
                         new OrgSubType(7,'subT7')])]),
            
            selectedOrgTypeId = ko.observable(1),
            selectedOrgType = ko.computed(function(){
               var otList = orgTypes(),
                   selId = selectedOrgTypeId();
                for(var i =0; i<otList.length; i++){
                    if(otList[i].id()==selId)
                        return otList[i];
                }
                return null;                   
            }),
            
            selectedOrgSubTypeId = ko.observable(1),
            selectedOrgSubType = function(){
               var otList = selectedOrgType().subTypes(),
                   selId = selectedOrgSubTypeId();
                for(var i =0; i<otList.length; i++){
                    if(otList[i].id()==selId)
                        return otList[i];
                }
                return null;                   
            };                           

        return {
            orgTypes  : orgTypes,
            selectedOrgTypeId : selectedOrgTypeId,
            selectedOrgType  : selectedOrgType,
            selectedOrgSubTypeId  : selectedOrgSubTypeId,
            selectedOrgSubType : selectedOrgSubType
        };            
    }


  • Таким образом главная модель предоставляет список типов организаций посредством свойства orgTypes
  • В свойстве selectedOrgTypeId хранится значение идентификатора выбранного типа организации
  • Из свойства selectedOrgType получаем объект типа организации *
  • Посредством selectedOrgSubTypeId получаем идентификатор выбранного суб типа организации
  • Посредством свойства selectedOrgSubType - получает объект суб типа организации


Html код



<div>
    <label>Organization type:</label>
    <select data-bind="options: orgTypes, optionsText: 'name', optionsValue: 'id', value: selectedOrgTypeId">
    </select>
     <span data-bind="text: selectedOrgType().name"></span>
</div>
<div>
    <label>Organization sub type:</label>
    <select data-bind="options: selectedOrgType().subTypes, optionsText: 'name', optionsValue: 'id', value: selectedOrgSubTypeId"></select>
    <span data-bind="text: selectedOrgSubType().name"></span>
</div>



Заметки


В ходе реализации я наткнулся на неумение knockout выделять в раскрывающемся списке выбранное значение и как оказалось и об этом писали на StackOwerflow. Решение данной проблемы заключается в использовании optionsText: 'name', optionsValue: 'id' конструкции маппинга в совокупности с value: selectedOrgTypeId. После установки параметров в html коде выбранное значение правильно устанавливается в ниспадающем списке. Но данное решение, накладывает свое ограничение - при этом не возможно просто получить доступ к другим значениям выбранного значения, кроме как к id.
И для решения проблемы, используется поиск значения по id в списке и последующий возврат его. Реализацию данного подхода можно увидеть в теле свойства selectedOrgType или selectedOrgSubType.

Но на этом не закончились интересные моменты. При попытке использовать ko.computed для свойства selectedOrgSubType, реализация не работала, скорее всего по причине особенности работы самого метода. Только после того, как я убрал ko.computed, задач была решена.

ОБНОВЛЕНИЕ

Очень признателен xdenser за его реализацию задачи. Благодаря его решению нашел недостаток в своем. При использовании optionsCaption: '', моя реализация не работает.
Потому привожу ссылку на реализацию xdenser Реализация пользователя xdenser на jsFiddle

Ниже приведенная реализация пользователя xdenser, работает отлично с указанием optionsCaption: '_значение_'

Реализация задачи от xdenser с применением биндинга по объекту
Javascript



$(function(){
    function OrgTypesModel(){
        var orgTypes = ko.observableArray([
            new OrgType(1,'tp1',
                        [new OrgSubType(1,'subT1'), 
                         new OrgSubType(2,'subT2'), 
                         new OrgSubType(3,'subT3')]),
            
            new OrgType(2,'tp2',
                        [new OrgSubType(4,'subT4'), 
                         new OrgSubType(5,'subT5'), 
                         new OrgSubType(6,'subT6'),
                         new OrgSubType(7,'subT7')])]),
            
           selectedOrgType = ko.observable(orgTypes()[1]),
                        
           selectedOrgSubType =  ko.observable(orgTypes()[1].subTypes()[1]);
        
           selectedOrgType.subscribe(function(val){
                selectedOrgSubType(null); 
                  
            });
             
        
        return {
            orgTypes  : orgTypes,
            selectedOrgType  : selectedOrgType,
            selectedOrgSubType : selectedOrgSubType
        };            
    }
            
    function OrgType(idv, namev, subTypesv){
        var id = ko.observable(idv),
            name = ko.observable(namev),
            subTypes = ko.observableArray(subTypesv);
        return {
            id : id,
            name : name,
            subTypes : subTypes
        };
    }
    
    function OrgSubType(idv, namev){
        var id = ko.observable(idv),
            name = ko.observable(namev);
        return {
            id : id,
            name : name            
        };         
    }
    
    ko.applyBindings(new OrgTypesModel());
});​


Html


<div>
<label>Organization type:</label>
<select data-bind="options: orgTypes, optionsText: 'name', value: selectedOrgType, optionsCaption: 'Select Type...'">
</select>
<span data-bind="text: selectedOrgType().name"></span>
</div>
<div>
<label>Organization sub type:</label>
<select data-bind="options: selectedOrgType().subTypes, optionsText: 'name', value: selectedOrgSubType, optionsCaption: 'Select SubType...'"></select>
<!-- ko with:  selectedOrgSubType   --> 
<span data-bind="text: name"></span>
<!-- /ko  -->     
</div>



ОБНОВЛЕНИЕ 2

Еще я бы хотел заострить внимание на работе с использованием привязки по свойству, которое сохраняет полностью выбранный объект (т.е. вот этот случай ). Тут есть одно "но", объект будет выделен в раскрывающемся списке, только в том случае, если он из той же коллекции, что была загружена в качестве источника данных, для ниспадающего списка. Knockout - сечет, что объект другой, что мне и понравилось очень четко отличает объекты, т.е. грамотно реализована схема сравнения объектов, а как следствие, не находит его в коллекции.

Если объект, а так бывает в реальных задачах, получается из другой модели, то есть смысл найти его по id из объектов коллекции ниспадающего списка и присвоить его свойству подвязанному на value:


Ссылки




Спасибо за внимание! Надеюсь данная статья будет интересна :)
Tags: JavaScriptKnockout.js
Hubs: JavaScript
Total votes 5: ↑5 and ↓0 +5
Comments 16
Comments Comments 16

Popular right now