Ian
Ian

Reputation: 34489

Knockout: Binding to value on click

I seem to be struggling to bind to an object on a click, and I can't figure out why. A fragment of my HTML is as follows:

     <div class="moodControlGroup">
        <label class="control-label" for="nodeHeight">Height</label>
           <div class="controls">
              <input type="text" class="query" id="nodeHeight" data-bind="value: settings.nodes.height.value" />
              <textarea class="queryExpanded funcText" data-bind="value: settings.nodes.height.funcValue"></textarea>
           </div>
           <input class="fx btn btn-primary" type="submit" value="fx" data-bind="click: function(item) { alert(JSON.stringify(item)) ; }, css: { selected: settings.nodes.height.isFunc }" />
     </div>

My problem lies in my submit button at the bottom. The settings has the following JSON structure - this is turned into an knockout object using ko.mapping:

nodes: {
                width: { isFunc: false, value: 24, funcValue: "" },
                height: { isFunc: true, value: 24, funcValue: "function(d) { return d.dy; }" }, 
}...

What I want to do is click on the submit button, and to get the height object:

height: { isFunc: true, value: 24, funcValue: "function(d) { return d.dy; }" }

It seems however I only ever get the empty object {}, or the object slightly higher up which contains the nodes property. How do I manage to target my click event to grab the right value? Ultimately I want to be able to toggle the state of the isFunc value behind it, to update some CSS... but I don't wish to use a checkbox control.

Can anyone suggest what I might be doing wrong?

Thanks

Upvotes: 0

Views: 1579

Answers (1)

BrandonLWhite
BrandonLWhite

Reputation: 1886

You need to create a context for KO if you want to leverage the 'this' value in the click handler. Since you aren't iterating a collection (foreach), then try using a 'with' binding, such as in the outer div, or if you just want to quickly try, throw a wrapper div around the submit button and data-bind a with on that.

Something like this:

<div data-bind="with: settings.nodes.height">
    <input class="fx btn btn-primary" type="submit" value="fx" data-bind="click: function(item) { alert(JSON.stringify(item)) ; }, css: { selected: isFunc }" />
</div>

I'm not sure what your final code is going to look like, but if all you are ever going to do in this block is bind to settings.nodes.height, then most likely you will want to put the 'with: settings.nodes.height' on the outermost div. Then you can reduce all of the descendant bindings to just the properties within the height object. Like this:

<div class="moodControlGroup" data-bind="with: settings.nodes.height">
        <label class="control-label" for="nodeHeight">Height</label>
           <div class="controls">
              <input type="text" class="query" id="nodeHeight" data-bind="value: value" />
              <textarea class="queryExpanded funcText" data-bind="value: funcValue"></textarea>
           </div>
           <input class="fx btn btn-primary" type="submit" value="fx" data-bind="click: function(item) { alert(JSON.stringify(item)) ; }, css: { selected: isFunc }" />
     </div>

Update: After our short discussion in the comments, I decided to paste your code into a jsFiddle.

This fiddle uses no 'with' binding on the outer div, thus the root ViewModel is what the click handler's 'this' is bound to (which sounds like what you don't want). I added an 'fx2' button that will dump 'this' using ko.mapping.toJSON. As discussed in the comments, JSON.stringify won't show your observable properties which are actually functions at this point.

This other fiddle introduces a data-bind="with: settings.nodes.height" on the outer div. Thus, the click handler's 'this' now refers to the settings.nodes.height object, which I believe is what you wanted.

Upvotes: 1

Related Questions