knockoutjs: can we create a dependentObservable function with a parameter?
Asked Answered
K

3

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')">
Khrushchev answered 15/7, 2011 at 11:21 Comment(0)
L
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);
Lee answered 15/7, 2011 at 19:47 Comment(2)
thank you. It worked beautifully. You saved me a lot of time.Khrushchev
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.Zygapophysis
S
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.

Saurischian answered 15/7, 2011 at 15:44 Comment(1)
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.Lyons
P
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)();
};
Pinhead answered 10/7, 2012 at 12:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.