quickshiftin
quickshiftin

Reputation: 69611

Knockout JS foreach $root undefined

I'm trying to create a radio group in Knockout JS, here is the template code

<p>Selected Plan <div data-bind="text: selectedPlan"></div></p>
<div data-bind="foreach: plans">
    <label>
        <input type="radio" name="plan" data-bind="attr: {value: id}, checked: $root.selectedPlan"/>
        <span data-bind="html: title"></span>
        <div data-bind="text: desc"></div>
    </label>
</div>

In the component selectePlan and plans are registered as follows

this.plans = ko.observableArray([/* array of plans */]);
this.selectedPlan = ko.observable('xxxxx');

I have verified that xxxxx is a valid entry in this.plans. Despite this an error is raised in the console

knockout.js:3391 Uncaught TypeError: Unable to process binding "foreach: function(){return plans }" Message: Unable to process binding "checked: function(){return $root.selectedPlan }" Message: Cannot read property 'selectedPlan' of undefined

It seems the $root keyword is undefined for some reason...

EDIT: Structure of plans

[{
 id: 'xxxxx',
 desc: 'This is a great plan',
 title: '<strong>Save with great plan</strong>',
},
...
]

Upvotes: 0

Views: 1100

Answers (2)

Sam
Sam

Reputation: 1707

I know this has been answered, but I'm thinking you're still wondering why $root is undefined(I would). I can see you're talking about a 'component' here and there. Might it be as simple as you just trying to get the root of the component, thus $component?

from the knockout site:

$component

If you’re within the context of a particular component template, then $component refers to the viewmodel for that component. It’s the component-specific equivalent to $root. In the case of nested components, $component refers to the viewmodel for the closest component.

This is useful, for example, if a component’s template includes one or more foreach blocks in which you wish to refer to some property or function on the component viewmodel rather than on the current data item.

Please view the Knockout Binding Context page for more info.

Upvotes: 2

Akrion
Akrion

Reputation: 18515

Take a look at this example:

<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<p>Selected Plan
  <b><span data-bind="text: selectedPlan"></span></b>
</p>
<div data-bind="foreach: plans">
  <label>
    <input type="radio" name="plan" data-bind="value: title, checked: $parent.selectedPlan"/>
    <span data-bind="html: title"></span>
    <span data-bind="text: desc"></span>
  </label>
</div>

<script type="text/javascript">
  var viewModel = {
    plans: ko.observableArray([
      {id: 1, desc: 'Red Foo', title: 'Foo'}, 
      {id: 2, desc: 'Blue Bas', title: 'Bas'}
    ]),
    selectedPlan: ko.observable()
  };
  ko.applyBindings(viewModel);
</script>

Few changes I made:

  1. Did not use attr binding and did instead straight value and checked
  2. I used $parent to get to the top level but $root works as well
  3. I changed the html a little bit to get it on one line etc.

Upvotes: 1

Related Questions