I'm experiencing with knockout.js components and require.js. This is working well so far, but I'm struggling with the following.
Let's say I have one instance of my component in a very simple html page :
<div id="exams">
<databound-exam-control></databound-exam-control>
</div>
From the containing viewmodel :
require(['knockout', 'viewModel', 'domReady!'], function (ko, viewModel) {
ko.components.register('databound-exam-control', {
viewModel: { require: 'databound-exam-control-viewmodel' },
template: { require: 'text!databound-exam-control-view.html' }
});
ko.applyBindings(new viewModel());
});
I would like to get the child viewmodel content to later save all the data of the page when I click a button.
For now I just trying to display the display the parent/child viewmodels in a pre tag :
<div>
<pre data-bind="text: childViewModel()"></pre>
</div>
With the help of the containing viewmodel :
function childViewModel() {
var model = ko.dataFor($('databound-exam-control').get(0).firstChild);
return ko.toJSON(model, null, 2);
};
I get the following error during the call to ko.dataFor, probably because the the page is not completely rendered :
Cannot write a value to a ko.computed unless you specify a 'write' option. If you wish to read the current value, don't pass any parameters.
Is that it ? Or am I completelty missing the point here ?
Any help appreciated.
The easier way to communicate between the parent viewmodel and the child component is by using parameters.
childViewModel = ko.observable()
<databound-exam-control params= "{modelForParent: childViewModel}">
Note that the parameter inside the child viewmodel will be known as modelForParent
, and in the parent view model will be known as childViewModel
databound-exam-control-viewmodel.js
script, you receive the parameters as the only argument for your constructor. So, if your constructor looks like this:
function SomeComponentViewModel(params)
you can access the parameter as params.modelForParent
params.modelForParent(createdChildViewModel)
.Now, the parent component can acces the child view model using the childViewModel
observable.
Using observables is just one possibility. You can do it in other ways. For example pass a callback as parameter, and execute that callback in the viewmodel constructor to return whatever you want to the parent. I sometimes use this pattern:
registerApi = function(api) { config.childApi = api }
In this way, I can acces the api exposed by the child component like this: config.childApi.aMethosExposedByTheChild()
Important note: you must take into account that, as the child component loads asynchronously, the information exposed by the child is not immediately available.
Unless you need to use it immediately this isn't a problem. For example, in your case, it looks like you'll get information from the child viewmodel after the component has been loaded and the user has interacted with it, so that's not a problem.
If you need to access it as soon as possible you can use polling --- or better, expose a deferred (an example implementation: $.Deferred) to the child so that it can resolve it to let the parent veiw model know it's already available. This also happens when the child viewmodel depends on loading external resources by AJAX calls (for example to load a drop down list, or some other information existing on the server).
Another option, which I don't like, is that the parent viewmodel includes the whole child view model and passes it as parameter, so the parent viewmodel has full control on the child view model. Obviously this solution doesn't allow the component to be responsible for its own viewmodel, so there is a tight coupling between parent viewmodel and child component, which is not desirable.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With