I have a JSON object that I used to create a form. This JSON object is parsed by KnockoutJS.
Now, when I modify the form, I want the JSON object to be updated according to the modifications made in the form. The thing is that I don't know in advance how the form will be like but I know in the JSON Object which fields need to be updated.
I really don't know what is the best way to procede. I know that I could reconstruct the JSON Object each time something has changed but this seems like a bad idea and a tedious process.
Is there a simple way to map each JSON Object field to form items in KnockoutJS ?
Here's a JSFiddle of what I'm currently doing:http://goo.gl/ZBaV7
Update :
I realized something interesting with this line:
<input type="text" data-bind="value: $data.value, attr : { disabled: $data.disabled }" />
I'm accessing the value directly from the array via ($data.value). Is there a way in the html to say to knockout to bind to this particular attribute in the array. I know that if the array would get reordered everything would get messed up but since I know that the only thing that can changed is this property I'm ready to take this risk ?
In other words, is there a way to manually say that when this value changes to change it in the array such as
data-bind="onChange: $data.value = this.value"
Is there a simple way to map each JSON Object field to form items in
KnockoutJS ?
Yes, If I understand what you want to do correctly. As of now, the values in your view model are not observables and won't be updated automatically as the form values change. There is a plugin to handle this mapping.
http://knockoutjs.com/documentation/plugins-mapping.html
Example: Using ko.mapping
To create a view model via the mapping plugin, replace the creation of
viewModel in the code above with the ko.mapping.fromJS function:
var viewModel = ko.mapping.fromJS(data);
This automatically creates observable properties for each of the
properties on data. Then, every time you receive new data from the
server, you can update all the properties on viewModel in one step by
calling the ko.mapping.fromJS function again:
ko.mapping.fromJS(data, viewModel);
Hopefully this helps.
If your Knockout ViewModel matches your form, you could just use the built in ks.toJSON()
http://knockoutjs.com/documentation/json-data.html
A better option, especially if your form is large or complex, is to use either the mapping or viewmodel plug-ins
http://knockoutjs.com/documentation/plugins-mapping.html
http://coderenaissance.github.io/knockout.viewmodel/
The simplest way to turn your json model into a usable knockout model is with the mapping plugin.
Alternatively, you can manually copy fields from your json model into ko.observable members of your view model, which give you more control and lets you choose to skip read-only properties.
Related
See twiddle here: https://ember-twiddle.com/2150099882893760cef237ff2bd22e85
Basically, in crit-service I create Ember Objects "Crits" and "Crit", and fill them with some data.
The crit-service is used by two different components, which basically do the same thing: display the Crits.
The problem is that the "change" buttons do not work. By debugging, I see that the values are changed, but the view is not updated. Why is this? Since Ember.Object is Observable, shouldn't setting a value notify the template? And most importantly, how can I get this to work?
P.S. I've seen a workaround by using Ember.A() instead of Objects. However, this would add boilerplate, as my data model is really objects and not arrays of key-value pairs.
This seems to be an issue with the {{#each-in}} helper which does not reload on changes. A quick fix is to use the {{get}} helper.
So instead of this:
{{#each-in obj as |key val|}}
{{key}}={{val}}
{{/each-in}}
Do this:
{{#each-in obj as |key|}}
{{key}}={{get obj key}}
{{/each-in}}
However, this will never work if you add additional properties.
here is a working twiddle with that solution.
Another solution that will always work is to call .rerender() on the component. This is save thanks to glimmer, which does only update the parts of the DOM that have changed. However, you would have to call it on your common root component of the two components, or on both components.
I've to put two ko.applyBindings(); in the same function but the system turn me back a run-time error.
Is it possible to do that?
Usually, you create a viewModel object and then call ko.applyBindings(viewModel) once, like this:
var viewModel = {
personName: ko.observable('Bob'),
personAge: ko.observable(123)
};
ko.applyBindings(myViewModel);
Maybe this is what you're looking?
Optionally, you can pass a second parameter to define which part of the document you want to search for data-bind attributes. For example, ko.applyBindings(myViewModel, document.getElementById('someElementId')). This restricts the activation to the element with ID someElementId and its descendants, which is useful if you want to have multiple view models and associate each with a different region of the page.
From the documentation here.
As for your comment about managing a list and an array, I'm not sure what you mean. They sound like the same thing to me. Knockout has observable arrays that you can use.
I have a viewModel that looks something like this when simplified:
var viewModel = function(){
var self = this;
self.selectedObject = ko.observable({});
self.getUnit = function(){
//get the selected object from the server side as json
self.selectedObject(ko.mapping.fromJS(data,mapping));
};
self.addObjectMember = function(){
self.selectedObject().objectMembers.push(new ObjectMemberViewModel(null, self.selectedObject()));
self.save = function(){
var data = ko.mapping.toJS(self.selectedObject);
//ship data to server
}
The data received from the server contains an array called objectMembers that has some properties inside it. The properties might differ between different objects.
My mapping object look like this:
var mapping = {
'objectMembers': {
create: function(options){
return new ObjectMemberViewModel(options.data, options.parent);
}
}
};
To see my problem, I'll give an example:
The user loads the page, and then fetches an object. That object includes two elements inside the objectMembers array. Then those are mapped using the ko.mapping.fromJS and everything works just fine. I can modify my viewModel using my model and the viewModel is updated. Then the user clicks a button that triggers the addObjectMember function, adding a third entry to the observableArray. I can interact with this also, and any changes done to my model can be seen in the viewModel as expected.
The problem comes when I click save. If I debug the save method, and check the contents of self.selectedObject, I can see that it contains what I want it to, but the object that is mapped back into the variable data has the last element in the objectsMembers array as an empty object ({}). The two other object look as I want them to.
I think I know why. The two first object have their __ko_mapping__.mappedProperties containing all the properties they had when it got mapped initially. The last one however has an empty __ko_mapping__.mappedProperties, and therefore I guess that no properties are mapped back.
So I need to do one of these things I guess:
When adding the object to the array on addObjectMember I need to get the __ko_mapping__.mappedProperties set so that it gets mapped back when I save.
When mapping back, I include all the properties on the mapped object regardless of their presence inside the __ko_mapping__.mappedProperties.
I have no clue how to do any of them that does not feel like a dirty hack, so any help here would be appreciated.
I ended up using ko.toJS instead of ko.mapping.toJS. ko.toJS does not care about __ko_mapping__, and maps everything (including __ko_mapping__).
That means it maps a bit more than I need, but other than that it works just fine.
I have a javascript object (viewModel) that is binded to view . I'm getting new version of it (newViewModel) from server . (You can imagine stock market price application at this point)
I don't want to bind newViewModel because I dont want to loose old viewModel's reference.
So I need update all properties of my existing javascript object from server response !
This is maybe javascript question but my viewModel have so many observable properties and sub-properties. (knockout's observable)
Ps: jQuery's $.extend does not work.
UPDATED (It seems ok):
ko.mapping.fromJS(newViewModel,viewModel); seems working .. I could not recognize it, because "Vs.Net Intellisense" did not show overloads of it.
My object is very deeply structured. if I see any problems I will inform you.
UPDATED (Does not work):
viewModel.x[] length=2
newViewModel.x[] length=12
The code above replaces all x with new ones. (I lost viewModel.x[0],viewModel.x[1] references) !!
So my html is not synchronized with my objects. viewModel.x[0] in the basket on the right side but it
is not in the basket on the left side (screen)
There is no magic way for it.. Because I need to match old array elements with news. "Code" needs to know "keys" of array elements..
I visit all array elements if I could find newElement in my old array I update oldElement's properties if I could not find I add newElement to oldArray.
Painful but I had to do.
I need to know how to trigger the update for an observableArray when an observable is changed inside the observableArray.
I have an observableArray that represents a binary tree. I'm using a storage mapping function to get and set the values in the array so it has to be balance even thought it might only contain an empty observable. E.g. nodes()[9] maybe null but when that node is updated I would call nodes()[9](set new value) and need to trigger the observableArray to update
Ended up using .replace() on the observableArray
Managed to look through the code and find observableArray.replace ()
RELATED: How to replace a given index element in knockoutjs