23

I have multiple input boxes that I want to hide/unhide based on a selection from user.

I can achieve this by having a separate dependentObservable for each input and in turn making the dependentObservable observe the parent selection.

viewModel.showField1= ko.dependentObservable(function () {
    return viewModel.selectedType() ? IsFeatureVisible(viewModel, "BusinessFieldName1") : false;
}, viewModel
);

viewModel.showField1= ko.dependentObservable(function () {
    return viewModel.selectedType() ? IsFeatureVisible(viewModel, "BusinessFieldName2") : false;
}, viewModel
);

this is kind of tedious to do for each field. Can I bind the elements with a dependentObservable function that can take a parameter? Important thing is it should get triggered when the parent changes

Other option is that when the parent changes, I loop through the elements and hide/unhide but that will require me to map the element id <-> business name of the field.

Current

 <tr data-bind="visible: showField1">
 <tr data-bind="visible: showField2">

Desired

<tr data-bind="visible: showField('BusinessFieldName1')">
<tr data-bind="visible: showField('BusinessFieldName2')">
Pinakin Shah
  • 877
  • 2
  • 12
  • 25

3 Answers3

31

In Knockout, bindings are implemented internally using dependentObservables, so you can actually use a plain function in place of a dependentObservable in your bindings. The binding will run your function inside of a dependentObservable, so any observables that have their value accessed will create a dependency (your binding will fire again when it changes).

Here is a sample: http://jsfiddle.net/rniemeyer/2pB9Y/

html

type "one", "two", or "three": <input data-bind="value: text" /> 
<hr />
<ul data-bind="template: { name: 'itemTmpl', foreach: items }"></ul>

js

<script id="itemTmpl" type="text/html">
    <li data-bind="text: name, visible: viewModel.shouldThisBeVisible(name)"></li>
</script>

var viewModel = {
    text: ko.observable("one"),
    items: [{name: "one"}, {name: "two"}, {name: "three"}],
};

viewModel.shouldThisBeVisible = function(name) {
    return this.text() === name;
}.bind(viewModel);

ko.applyBindings(viewModel);
Andrew
  • 18,680
  • 13
  • 103
  • 118
RP Niemeyer
  • 114,592
  • 18
  • 291
  • 211
  • thank you. It worked beautifully. You saved me a lot of time. – Pinakin Shah Jul 16 '11 at 02:46
  • 2
    Just a note to update anyone that comes along. In more recent versions of knockout there is no need to use a `template` in this case, you can just use `foreach`. I've seen developers using `template` because they din't understand this. There is nothing special about the template binding that enables this. – George Mauer Aug 01 '13 at 17:14
12
var someOtherViewModel = {
   showField: function(fieldName) {
       return ko.dependentObservable(function () {
           return viewModel.selectedType() ? IsFeatureVisible(viewModel, fieldName) : false;
       }, viewModel);
   }
};

You can create a function like the one above. The function returns a new dependent observable for the specific field name.

Now you can do:

<tr data-bind="visible: someOtherViewModel.showField('Field1')">

Inform me if that code doesn't work - maybe I missed something. Then I'll edit this post.

Arxisos
  • 2,719
  • 20
  • 21
  • This is cool although I think you have to be careful using this. Say if you are calling showField in a loop with 100s of records, you are essentially creating a 100 dependentObservables in memory. In that case it would be better to just define showFieldX, showFieldY, etc in your VM given they are not in the 100s to keep your client side memory footprint low. – jesal Mar 21 '15 at 15:37
0

Taking the idea from @Arxisos even further, I came up with this.

self.showField = function (fieldName)
{
    return ko.dependentObservable(function () 
    {
        return this.selectedType() ? IsFeatureVisible(this, fieldName) : false;
    }, this)();
};
Johann Strydom
  • 1,482
  • 14
  • 18