1

I have the following code from the JSFiddle I have setup: https://jsfiddle.net/ktce56hr/2/

var RootViewModel = function() {
    var self = this;
    this.rootText = ko.observable('default text');
    this.rootFunc = function() {
        alert("root func!");
    };
}

ko.components.register('root', {
    viewModel: function(model) {
        var self = this;
        this.rootViewModel = model;
        this.title = ko.observable('default title');
    },
    template: { element: 'root_template' }
});

$(function () {
    ko.applyBindings(new RootViewModel());
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.2/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.3.0/knockout-min.js"></script>

<div data-bind="component: { name: 'root', params: { model: $root } }"></div>

<script id="root_template" type="text/html">
    <div>
        <span data-bind="text: title"></div>
        <div data-bind="with: rootViewModel">
            <input type="text" data-bind="value: rootText" />
            <button data-bind="click: rootFunc">Go</button>
        </div>
    </div>
</script>

When this code is run I receive the following error:

Uncaught ReferenceError: Unable to process binding "with: function (){return rootViewModel }"
Message: Unable to process binding "value: function (){return rootText }"
Message: rootText is not defined

Can someone please explain why I can't set the binding context using the 'rootViewModel' property on my component view model?

Tomalak
  • 332,285
  • 67
  • 532
  • 628
Alex Hope O'Connor
  • 9,354
  • 22
  • 69
  • 112

1 Answers1

1

You are declaring the component instance like so:

<div data-bind="component: { name: 'root', params: { model: $root } }"></div>

params will be the first argument to the viewmodel constructor function. To the viewmodel constructor it looks like this:

"model": {
  "rootText": "default text"
}

and the viewmodel it returns will be this:

{
  "rootViewModel": {
    "model": {
      "rootText": "default text"
    }
  },
  "title": "default title"
}

Of course, accessing title in your view works. But inside with: rootViewModel it won't contain rootText.

The fix is trivial:

<div data-bind="component: { name: 'root', params: $root }"></div>

var RootViewModel = function() {
    var self = this;
    this.rootText = ko.observable('default text');
    this.rootFunc = function() {
        alert("root func!");
    };
}

ko.components.register('root', {
    viewModel: function(model) {
        var self = this;
        this.rootViewModel = model;
        this.title = ko.observable('default title');
    },
    template: { element: 'root_template' }
});

$(function () {
    ko.applyBindings(new RootViewModel());
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.2/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.3.0/knockout-min.js"></script>

<div data-bind="component: { name: 'root', params: $root }"></div>

<script id="root_template" type="text/html">
    <div>
        <span data-bind="text: title"></div>
        <div data-bind="with: rootViewModel">
            <input type="text" data-bind="value: rootText" />
            <button data-bind="click: rootFunc">Go</button>
        </div>
    </div>
</script>

P.S.: There still is a similar problem with rootFunc, but I trust you can sort that out.

Tomalak
  • 332,285
  • 67
  • 532
  • 628
  • Thanks heaps, misinterpreted another example in the documentation and was stuck with this for longer then I should have been.... – Alex Hope O'Connor Nov 11 '15 at 06:46
  • 1
    Here's a tip: `
    `. Yes, there are more sophisticated tools to debug Knockout (for example the [Knockoutjs context debugger Chrome extension](https://chrome.google.com/webstore/detail/knockoutjs-context-debugg/oddcpmchholgcjgjdnfjmildmlielhof?hl=en)), but simply dumping the current viewmodel to the page is surprisingly effective.
    – Tomalak Nov 11 '15 at 06:57