Artur Cichosz
Artur Cichosz

Reputation: 1054

Losing view model context in a knockout.js component loaded by requirejs

I have a strange problem with knockout js component loaded by require js. The component is very simple.

define([
    'jquery',
    'knockout'
], function ($, ko) {


    function ViewModel(params) {
        var self = this;
        this.test = 'test';
    }

    ViewModel.prototype.fetchData = function() {
        var self = this;
        console.log(this.test);
    };

    ViewModel.prototype.init = function() {
        console.log(this.test);
        this.fetchData();
    };

    ko.components.register('my-component', {
        viewModel: ViewModel,
        template: '<span data-bind="template: { afterRender: init }"></span>'
    });

});

Now, the template binding is able to call the view models init method, but this method itself can not access the fetchData method, because inside the method this is bound to the Window object instead of the view model.

I prepared a fiddle to show the error case: http://jsfiddle.net/ask4artur/3f6jsa4m/47/

Upvotes: 1

Views: 264

Answers (1)

user3297291
user3297291

Reputation: 23372

I believe knockout does not call the afterRender method in the this-context of $data automatically. You can set $data as this by using bind like so:

data-bind="template: { afterRender: init.bind($data) }

var i = 0;

var ViewModel = function() {
   this.nr = "Instance " + i++; 
}

ViewModel.prototype.logNr = function() {
    console.log(this.nr);
};

ko.components.register('test', {
    viewModel: ViewModel,
    template: '<div data-bind="template: { name: \'testTemplate\', afterRender: logNr.bind($data) }"></div>'
});

ko.applyBindings();
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

<div data-bind="component: {name: 'test'}"></div>
<div data-bind="component: {name: 'test'}"></div>
<div data-bind="component: {name: 'test'}"></div>
<div data-bind="component: {name: 'test'}"></div>
<div data-bind="component: {name: 'test'}"></div>


<script type="text/html" id="testTemplate">
  <div data-bind="text: nr, click: logNr"></div>
</script>

Upvotes: 3

Related Questions