Danno
Danno

Reputation: 1003

How to generically hook up dependent drop-downs with knockout

I have a json structure like the following:

var scenario = {
    paints: [
       {
            product: 'Super Paint',
            sheens: [
                { sheen: 'Satin', cost: 42 },
                { sheen: 'Semi-Gloss', cost: 50 }
            ]
        },
        {
            product: 'Cashmere',
            sheens: [
                { sheen: 'Flat', cost: 42 },
                { sheen: 'Medium Lustre', cost: 50 }
            ]
        }
    ],
    walls: {
      "sheen":"Flat",
      "paintProduct":"Cashmere",
    },
    ceiling: {
      "sheen":"Flat",
      "paintProduct":"Cashmere",
   }
};

I figured out now to make drop-downs dependent for a given paintable item with the following:

function getSheens(paintProduct) {
    if (paintProduct) {
        for (var i = 0; i < self.scenario.paints.length; ++i) {
            if (self.scenario.paints[i].product === paintProduct) {
                return self.scenario.paints[i].sheens;
            }
        }
    }
    return null;
}

self.scenario.walls.sheens = ko.computed(function() {
    return getSheens(self.scenario.walls.paintProduct());
});

self.scenario.ceiling.sheens = ko.computed(function() {
    return getSheens(self.scenario.ceiling.paintProduct());
});

Here's the html:

<div class="item edit-walls" data-bind="with: scenario.walls">
    <label>Product</label>
    <select class="form-control paint-product" data-bind="options:$parent.scenario.paints, optionsText:'product', optionsValue:'product', value:paintProduct"></select>

    <label>Sheen</label>
    <select class="form-control paint-sheen" data-bind="options:$parent.scenario.walls.sheens, optionsText:'sheen', optionsValue:'sheen', value:sheen"></select>
</div>

The changing of the paint product for a given item, should reload the sheens of the newly select paint product.

The selected values for the paint product and sheen for the item, i.e. walls, are stored in the walls object.

What I would like to avoid is the repeating of the computed knockout call for each different item type. Is there a way to have this happen globally and pass in the context somehow?

I'm using knockout.js, knockout.viewmodel.js and jQuery.

Upvotes: 0

Views: 33

Answers (1)

Jeroen
Jeroen

Reputation: 63830

You're basically asking if it's possible to normalize "walls" and "ceiling" into one concept. You surely can. Let's call them paintables. Change your data structure to something like this:

var scenario = {
    paints: [/* omitted */],
    paintables: [
        { 
          "name":"walls",
          "sheen":"Flat",
          "paintProduct":"Cashmere"
        },
        { 
          "name":"ceiling",
          "sheen":"Flat",
          "paintProduct":"Cashmere"
    }]
};

You can then have a Paintable constructor function which implements the logic you have for sheens in a DRY manner.

Upvotes: 1

Related Questions