Reputation: 3715
I'm not sure I understand the workflow Knockout.js follows internally. I have a ViewModel that is defined dynamically, using the mapping plugin. Its shape is:
{ current: { foo: 'foo', bar: 'bar', id: 0 }
, ids: [1,2,3,4,5]
}
Each of those ids
corresponds to a value that can belong in current
. Now, the complication comes in because in the initial ajax request to populate my viewModel, the server responds with current: null
. I have written code to take the first id
out of the list of ids
and run another ajax query to populate current
. That looks like this:
var ViewModel = function (data) {
var self = this;
ko.mapping.fromJS(data, {}, self);
self.head = function () {
if (self.promptIds().length != 0) {
var nextId = _.head(self.promptIds());
self.promptIds(_.tail(self.promptIds()));
$.getJSON("my-url" + "/" + nextId, function (data) {
self.current(ko.mapping.fromJS(data));
});
}
}
}
$('document').ready ( function () {
$.getJSON("view-model-route", function (data) {
viewModel = new ViewModel(data);
viewModel.head();
ko.applyBindings(viewModel);
});
});
Notice that in the second block, I create a new ViewModel
object and I call head()
on it. Once the viewModel.current()
object is populated, I call ko.applyBindings
on the complete viewModel
. I can confirm that when all the dust settles, viewModel.current
is not null
. But I am still getting the error:
Uncaught TypeError: Unable to parse bindings.
Bindings value: text: current().foo
Message: Cannot read property 'foo' of null
Am I trying to do something Knockout wasn't designed to do? Do I need another layer of abstraction? How would you approach this situation?
Upvotes: 1
Views: 259
Reputation: 139758
The bindings are first evaluated when you call ko.applyBindings
.
So if your current
property contains null
by that time then this it is normal that you get an Cannot read property 'foo' of null
exception.
There are multiple solution to this problem:
Initialize current
with an empty object, so the current().foo
will be parsed correctly (but its value be undefined
):
viewModel = new ViewModel(data);
viewModel.head();
viewModel.current({});
ko.applyBindings(viewModel);
You can add an extra condition in your binding to check that current
has some value:
<input type="text" data-bind="value: current() && current().foo" />
Or probably the best and most recommended option is to use the with
binding if you are dealing with nested proeprties:
<!-- ko with: current -->
<input type="text" data-bind="value: foo" />
<!-- /ko -->
Demo JSFiddle.
Upvotes: 2