Vinit Divekar
Vinit Divekar

Reputation: 928

Dynamic binding of fields with Knockout

Please see the following example I have created. https://jsfiddle.net/divekarvinit/5vh67sod/4/

In this example, the 'Allowances' model contains various attributes. Out of these attributes, for a particular allowance, which attributes are to be shown on screen are contained in 'fields' array.

In the following HTML code,

<div data-bind="foreach : allowanceList">
  <div style="display : block; width : 100%">
    <span data-bind="text : allowanceTitle"></span>
  </div>
  <div style="margin-left : 20px" data-bind="foreach : fields">
    <div style="display : block; width : 100%">
      <label data-bind="text : title"></label>
      <!-- ko if : typeOfField() == "input"-->
      <input data-bind="value : '$parentContext.' + allowanceVariable() " />
      <!-- /ko -->
    </div>
  </div>
  <br/>
</div>

I want to bind <input> to the attribute of allowanceList's object whose name is contained in the value of 'allowanceVariable' variable of 'fields' array.

For example, for the first iteration of allowanceList and first iteration of fields, the allowanceVariable() has value of 'hours'. Hence, for this iteration, I want to bind the <input> to 'hours' field of the allowanceList's current object.

As seen in the fiddle, at present, knockout only copies $parentContext.hours into input.

How can I achieve the desired result?

Upvotes: 1

Views: 171

Answers (2)

adiga
adiga

Reputation: 35261

In your answer, you are hardcoding the allowanceVariables and checking them individually using if-else chains. For every property you add, you'd have to update the if-else block. It works, but they are no longer dynamic fields.

Instead, you can use the keyword as and give an alias to your foreach binding like this:

<div data-bind="foreach: { data: allowanceList, as: '_allowance'}">
  <div style="display : block; width : 100%">
    <span data-bind="text : allowanceTitle"></span>
  </div>
  <div style="margin-left : 20px" data-bind="foreach: { data: _allowance.fields, as: '_field'}">
    <div style="display : block; width : 100%">
      <label data-bind="text : title"></label>
      <!-- ko if : typeOfField() == "input"-->
      <input data-bind="value : _allowance[_field.allowanceVariable()]" />
      <!-- /ko -->
    </div>
  </div>
</div>

Updated fiddle


Mind you, we can make it work without alias as well. Use the $parent and $data binding contexts like this:

<div data-bind="foreach : allowanceList">
  <div style="display : block; width : 100%">
    <span data-bind="text : allowanceTitle"></span>
  </div>
  <div style="margin-left : 20px; display : block; width : 100%" data-bind="foreach : fields">
    <div style="display : block; width : 100%">
      <label data-bind="text : title"></label>
      <!-- ko if : typeOfField() == "input"-->
      <input data-bind="value : $parent[$data.allowanceVariable()]" />
      <!-- /ko -->
    </div>
  </div>
  <br/>
</div>

Updated Fiddle

But, I think the former approach is cleaner and easily understandable. Great stuff on creating neat fiddles by the way :)

Upvotes: 1

Vinit Divekar
Vinit Divekar

Reputation: 928

By using function inside the model, the desired result can be achieved. Following is the fiddle which demonstrates the answer.

https://jsfiddle.net/divekarvinit/9fq59gfc/1/

Here, I have called dynamicValue function of the Field model of inner for loop and I have passed $parent object to it.

In the function, I have checked the value of allowanceVariable and accordingly returned value of corresponding variable of Allowance model.

Please suggest any other better solution if applicable as in this solution, I have to hard code names of attributes of Allowance model in the dynamicValue function, but, this definitely solves my problem.

Upvotes: 0

Related Questions