Reputation: 1944
I have the following code:
function ViewModel() {
var self = this;
self.newCategory = {
name: ko.observable(''),
canSubmit: function() {
return this.name() !== '';
},
submit: function() {
var data = 'name=' + this.name();
$.post('/api/createcategory', data, function(res) {
//...
});
}
};
}
And the HTML
<button type="button" data-bind="enable: newCategory.canSubmit(), click: newCategory.submit">Create category</button>
In the canSubmit()
function, this
refers to self.newCategory
as I'm expecting. However, for some reason in the submit()
function, this
refers to ViewModel()
Why is this?
Upvotes: 0
Views: 49
Reputation: 338228
Knockout calls functions in the context of the viewmodel that they are bound to, in your case the main viewmodel. Hint: The viewmodel for submit
is not the object that you assigned to newCategory
.
In your binding you do two different things:
enable: newCategory.canSubmit()
calls the function right-away, on newCateory
. So that's what this
is going to be inside the function.click: newCategory.submit
mentions the function, i.e. knockout creates a click handler that will run in the context of the current viewmodel.Note that canSubmit
actually ought to be observable.
When you change the binding context, for example using the with
binding, the behavior is like you would expect it.
<!-- ko with: newCategory -->
<button data-bind="enable: canSubmit, click: submit">Create category</button>
<!-- /ko -->
For sub viewmodels I like to create actual stand-alone constructors:
function Category() {
var self = this;
self.busy = ko.observable(false);
self.name = ko.observable('');
self.canSubmit = ko.computed(function() {
return !self.busy() && self.name() > '';
});
}
Category.prototype.submit = function() {
var self = this;
if ( !self.canSubmit() ) return;
self.busy(true);
$.post('/api/createcategory', {
name: this.name()
}).done(function(res) {
//...
}).always(function() {
self.busy(false);
});
};
function ViewModel() {
var self = this;
self.newCategory = new Category();
}
and
<!-- ko with: newCategory -->
<input type="text" data-bind="name">
<button data-bind="enable: canSubmit, click: submit">Create category</button>
<img src="spinner.gif" data-bind="visible: busy">
<!-- /ko -->
Upvotes: 2