I'm getting this kind of data from the server
{
FirmName: "Firm Name",
Address: "123 Address",
AltNames: [
"First Alt Name",
"Second Alt Name"
]
}
I'm using knockout mappings plugin to map the data to a viewModel with ko.mapping.fromJS(data, {}, this);
However, anytime I change any string array element, my changes are not propagated to the viewModel. What do I do to fix the problem?
My js:
function firmModel() {
//this is what I get from the server
var data = {
FirmName: "Firm Name",
Address: "123 Address",
AltNames: [
"First Alt Name",
"Second Alt Name"
]
};
ko.mapping.fromJS(data, {}, this);
}
var em = new firmModel();
ko.applyBindings(firmModel);
My html:
<div>
<label>Company</label>
<input type="text" data-bind="value: FirmName">
</div>
<div>
<label>Address</label>
<input type="text" data-bind="value: Address">
</div>
<br>
<h3>
Alt Names
</h3>
<ul data-bind="foreach: AltNames">
<li>
<input type="text" data-bind="value: $data">
</li>
</ul>
<br/><br/>
<h2>
Result of the changes where I see whether viewModel picks up my changes above
</h2>
<span>Company: </span><span data-bind="text: FirmName"></span><br/>
<span>Address: </span><span data-bind="text: Address"></span><br/>
<br/>
Alt Names: <br/>
<ul data-bind="foreach: AltNames">
<li data-bind="text: $data"></li>
</ul>
In the foreach
you can't use $data
:
<input type="text" data-bind="value: $data">
as you can read in documentation:
$rawData
This is the raw view model value in the current context. Usually this will be the same as $data, but if the view model provided to Knockout is wrapped in an observable, $data will be the unwrapped view model, and $rawData will be the observable itself.
Then, you need to use $rawData
instead, because the content of the AltNames
array must be observables, not strings.
The strings in the AltNames
must be observables. You need to customize the creation of the AltNames
array with custom object construction using “create”:
var mapping = {
'AltNames': {
create: function(options) {
return ko.observable(options.data);
}
}
}
ko.mapping.fromJS(data, mapping, this);
This is a runnable example:
function firmModel() {
//this is what I get from the server
var data = {
FirmName: "Firm Name",
Address: "123 Address",
AltNames: [
"First Alt Name",
"Second Alt Name"
]
};
//ko.mapping.fromJS(data, {}, this);
var mapping = {
'AltNames': {
create: function(options) {
return ko.observable(options.data);
}
}
}
ko.mapping.fromJS(data, mapping, this);
}
var em = new firmModel();
ko.applyBindings(firmModel);
h2{
font-weight: bold;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.1.0/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.min.js"></script>
<div>
<label>Company</label>
<input type="text" data-bind="value: FirmName">
</div>
<div>
<label>Address</label>
<input type="text" data-bind="value: Address">
</div>
<br>
<h3>
Alt Names
</h3>
<ul data-bind="foreach: AltNames">
<li>
<input type="text" data-bind="value: $rawData">
</li>
</ul>
<br/><br/>
<h2>
Result of the changes
</h2>
<span>Company: </span><span data-bind="text: FirmName"></span><br/>
<span>Address: </span><span data-bind="text: Address"></span><br/>
<br/>
Alt Names: <br/>
<ul data-bind="foreach: AltNames">
<li data-bind="text: $data"></li>
</ul>