Jon Hadley
Jon Hadley

Reputation: 5296

Knockout.js - Disabled, rather than hidden, cascading selects?

I've created a multiple, cascading selects with Knockout, using code inspired by the accepted answer in this question (jsFiddle - code at bottom of post too).

It works great, however I'd like to disable the child selects when they are unavailable, rather than hide them completely.

Is there a simple option for this?

The hide behaviour seems to be driven by the 'with' binding, but this seems integral to how the arrays are linked.

var mainViewModel = null;

var mainViewModelData = {
    "RequestName": "test Name",
    "BusinessLines": [{
        "Id": 1,
        "Title": "Retail",
        "Clusters": [{
            "Id": 1,
            "Title": "Corporate si Trezorerie"},
        {
            "Id": 2,
            "Title": "Good Bee"},
        {
            "Id": 3,
            "Title": "Leasing"}]},
    {
        "Id": 2,
        "Title": "Corporate",
        "Clusters": [{
            "Id": 1,
            "Title": "REM"},
        {
            "Id": 2,
            "Title": "BCR Procesare"},
        {
            "Id": 3,
            "Title": "Erste Asset Management"}]}],
    "SelectedBusinessLine": null,
    "SelectedCluster": null,
    "RequirementHighLevelDescription": null,
    "Revenues": 0,
    "Savings": 0,
    "PreventedLosses": 0
};

mainViewModel = ko.mapping.fromJS(mainViewModelData);

ko.applyBindings(mainViewModel);

<div>         
<table>            
  <tr>      
    <td>      
        Business Line    
      </td>
      <td>
          <select data-bind="options: BusinessLines,                                                         optionsText: 'Title',                                                           value: SelectedBusinessLine,  
                             optionsCaption: 'Select Business Line..'">
                 </select>           
             </td>
    </tr>

    <tr data-bind="with: SelectedBusinessLine">
        <td>
            Cluster
        </td>
        <td>              <select data-bind="options: Clusters,                                        optionsText: 'Title',                                                        value: $root.SelectedCluster,                                                        optionsCaption: 'Select Cluster..'">   
            </select>
        </td>
    </tr>
    </table>
</div>

Upvotes: 0

Views: 903

Answers (2)

Rango
Rango

Reputation: 3907

It's not so trivial but possible...

First you'd create the computed observable that will merge all the clusters of every BusinessLine with parent property that will keep cluster's parent BusinessLine object:

mainViewModel.AllClusters = ko.computed(function(){
    var result = [];
    ko.utils.arrayForEach(this.BusinessLines(), function(line){
        ko.utils.arrayForEach(line.Clusters(), function(cluster){
            result.push({ parent: line, Title: cluster.Title() });
        });
    });
    return result;
}, mainViewModel);

Then it's necessary to create new type of binding enabledOptions, that will set/remove "disabled" attribute for each cluster <option/> depending on parent property comparison result:

ko.bindingHandlers.enabledOptions = {
    update: function(element, valueAccessor, allBindings) {

        var options = element.getElementsByTagName('option'),
            parent = ko.utils.unwrapObservable(valueAccessor()),
            // delta is neccessary to strip select's caption if it exists
            delta = allBindings().optionsCaption ? 1 : 0;

        for (var i = delta; i < options.length; i++) {
            var cluster = mainViewModel.AllClusters()[i - delta];
            cluster.parent == parent
            ? options[i].removeAttribute('disabled')
            : options[i].setAttribute('disabled', 'disabled')
        }

    }
}

Finally remove with binding you've mentioned.

As a result you'll get something like this: http://jsfiddle.net/ostgals/x2qMg/28/

Upvotes: 1

pomber
pomber

Reputation: 23990

Remove the with binding, and bind the options binding to SelectedBusinessLine().Clusters.

Then use the disable binding to disable the select.

Upvotes: 0

Related Questions