Is it possible to get parent observable in knockout? Like I have
<div data-bind="parent: {...}">
<div data-bind="child: {...}">
...
</div>
</div>
I want to get access to parent in child but not in the markup but in the code. How can I achieve in a standard knockout way?
UPDATE
I have found a simple way to achieve this by simply accessing the last argument of custom handler like this:
ko.bindingHandlers.custom = {
init: function (element, valueAccessor, allBindingsAccessor, vm, bindingContext) {
console.log(bindingContext.$parent);
}
}
You can use $parent to access the parent item:
<div data-bind="parent: {...}">
<div data-bind="child: {...}">
<span data-bind="text: $parent.someObservable()"></span>
<span data-bind="text: somefunction($parent.someObservable())"></span>
</div>
</div>
The most simple way it to past parent object to child model as a parameter (pointer) when child object constructed. But it more standard javascript way then knockout
var Parent = function (item) {
var self = this;
this.value = ko.observable(item.value);
this.child = new Child(item.child, self);
}
var Child = function (item, parent) {
var self = this;
this.parent = parent;
this.value = ko.observable(item.value);
}
and HTML markup will look like
This is <b><span data-bind="text: value"></span></b>
<div data-bind="with: child">
This is <b><span data-bind="text: value"></span></b>
<br/>
This is <b><span data-bind="text: parent.value"></span></b> of <b><span data-bind="text: value"></span></b>
</div>
JSFIDDLE
To prevent big amount of code mapping plugin could be used and it will be more knockout way
var Parent = function (item) {
var self = this;
var map = {
'child': {
update: function(options) {
return new Child(options.data, self);
}
}
}
ko.mapping.fromJS(item, map, self);
}
var Child = function (item, parent) {
var self = this;
this.parent = parent;
ko.mapping.fromJS(item, null, self);
}
JSFIDDLE
And the most knockout way - it to create custom binding that will controls descendant bindings. In this way you can extend child context with extra properties.
ko.bindingHandlers.withParent = {
init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
// Make a modified binding context, with a extra properties, and apply it to descendant elements
ko.mapping.fromJS({
parent: bindingContext.$rawData
}, null,valueAccessor());
var childBindingContext = bindingContext.createChildContext(valueAccessor, null, null);
ko.applyBindingsToDescendants(childBindingContext, element);
// Also tell KO *not* to bind the descendants itself, otherwise they will be bound twice
return { controlsDescendantBindings: true };
}
};
Model
var Parent = function (item) {
var self = this;
var map = {
'child': {
update: function(options) {
return new Child(options.data);
}
}
}
ko.mapping.fromJS(item, map, self);
}
var Child = function (item, parent) {
var self = this;
ko.mapping.fromJS(item, null, self);
}
and HTML
This is <b><span data-bind="text: value"></span></b>
<div data-bind="withParent: child">
This is <b><span data-bind="text: value"></span></b>
<br/>
This is <b><span data-bind="text: parent.value"></span></b> of <b><span data-bind="text: value"></span></b>
<br/>
<input type="button" value="Test from code" data-bind="click: test"/>
</div>
JSFIDDLE
But personaly me not like this approach, because using together with 'with', 'foreach' or 'tempalate' bindings it could cause errors like
Message: You cannot apply bindings multiple times to the same element.
Related
I am having trouble populating an observableArray with a value inherited from a datepicker. I have a disabled textbox that displays the value of the datepicker as part of the data collection section. As it is disabled and not being typed in, it is not updating the observableArray.
I have created an example jsfiddle where I have stripped down and localised the problem.
Any help getting the value to appear in the observableArray would be great as I am really struggling to figure this one out!
HTML
<!--Date Load -->
<span><b>Select a date:</b></span>
<span><input id="theDate" data-bind="datepicker: viewModelWardStaff.dateMonthYear, datepickerOptions: { dateFormat: 'dd/mm/yy' } "></span>
<!--Input Form -->
<span><h4>Input New Entries</h4></span>
<div style="border: solid 1px;" data-bind="with: viewModelWardStaff">
<form class="grid-form" id="dataCollection">
<fieldset>
<div data-row-span="1">
<div data-field-span="1">
<label>Date</label>
<input id="cDate" class="autosend" data-bind="textInput: dateMonthYear, enable: false">
</div>
<div data-field-span="1">
<label>Status</label>
<input id="cStatus" maxlength="200" class="autosend" data-bind="textInput: wardstaff.Status" type="text">
</div>
</div>
</fieldset>
<div style="margin: 5px;">
<a style="margin-left: 300px;" id="addFileButton" class="button-link" data-bind="click: viewModelWardStaff.addEntry">Add</a>
</div>
</form>
</div>
<h4>View Model Ward Staff</h4>
<div data-bind="with: viewModelWardStaff">
<pre data-bind="text: ko.toJSON($data, null, 2)"></pre>
</div>
KnockoutJS
moment.locale('en-gb');
function WardStaff(data) {
var self = this;
self.Date = ko.observable(data.Date());
self.Status = ko.observable(data.Status());
};
function oWardStaff() {
var self = this;
self.Date = ko.observable();
self.Status = ko.observable();
};
var viewModelWardStaff = function () {
var self = this;
self.wardstaff = new oWardStaff();
self.dateMonthYear = ko.observable();
self.entries = ko.observableArray([]);
self.addEntry = function () {
self.entries.push(new WardStaff(self.wardstaff));
}
self.removeEntry = function (entry) {
self.entries.remove(entry);
}
};
// dateString knockout
ko.bindingHandlers.dateString = {
update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
var value = valueAccessor(),
allBindings = allBindingsAccessor();
var valueUnwrapped = ko.utils.unwrapObservable(value);
var pattern = allBindings.datePattern || 'YYYY-MM-DD HH:mm:ss';
if (valueUnwrapped == undefined || valueUnwrapped == null) {
$(element).text("");
}
else {
var date = moment(valueUnwrapped, "YYYY-MM-DDTHH:mm:ss"); //new Date(Date.fromISO(valueUnwrapped));
$(element).text(moment(date).format(pattern));
}
}
}
//datepicker knockout
ko.bindingHandlers.datepicker = {
init: function (element, valueAccessor, allBindingsAccessor) {
//initialize datepicker with some optional options
var options = allBindingsAccessor().datepickerOptions || {};
$(element).datepicker(options);
//WORK
//handle the field changing
ko.utils.registerEventHandler(element, "change", function () {
var observable = valueAccessor();
if (moment($(element).datepicker("getDate")).local().format('YYYY-MM-DD') == 'Invalid date') {
observable(null);
}
else {
observable(moment($(element).datepicker("getDate")).local().format('YYYY-MM-DD'));
}
});
//handle disposal (if KO removes by the template binding)
ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
$(element).datepicker("destroy");
});
},
//update the control when the view model changes
update: function (element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
current = $(element).datepicker("getDate");
if (moment(value).format('DD/MM/YYYY') == 'Invalid date') {
$(element).datepicker("setDate", null);
}
}
};
// Master View Model
var masterVM = function () {
var self = this;
self.viewModelWardStaff = new viewModelWardStaff();
};
// Activate Knockout
ko.applyBindings(masterVM);
I think the problem is that your observable date, dateMonthYear, lives in your master view model. The Date property of self.wardstaff is never set.
You could solve this by sharing the observable in your master view model with the one in the wardstaff property:
function oWardStaff(obsDate) {
var self = this;
self.Date = obsDate;
self.Status = ko.observable();
};
/* ... */
self.dateMonthYear = ko.observable();
self.wardstaff = new oWardStaff(self.dateMonthYear);
Now, whenever you pick a new date, it writes it to the observable referenced by both viewmodels.
This line suddenly becomes useful:
function WardStaff(data) {
var self = this;
self.Date = ko.observable(data.Date()); // <-- here
self.Status = ko.observable(data.Status());
};
since Date is now actually set.
Fiddle that I think now works correctly: https://jsfiddle.net/n0t91sra/
(let me know if I missed some other desired behavior)
I'm developing a 3 part upload form, where users can upload 3 sets of files
So far, I've got the following viewModel
var FileGroupViewModel = function (id) {
var self = this;
self.id = ko.observable(id);
self.files = ko.observableArray();
self.removeFile = function (item) {
self.files.remove(item);
}
self.fileUpload = function (data, e) {
var file = e.target.files[0];
self.files.push(file);
};
}
var ViewModel = function () {
var self = this;
self.fileGroups = ko.observableArray();
self.getFileGroupById = function (id) {
ko.utils.arrayFilter(self.fileGroups(), function (item) {
return item.id == id;
});
};
self.uploadFiles = function () {
alert('Uploading');
}
}
var viewModel = new ViewModel();
viewModel.fileGroups.push(new FileGroupViewModel(1));
viewModel.fileGroups.push(new FileGroupViewModel(2));
viewModel.fileGroups.push(new FileGroupViewModel(3));
ko.applyBindings(viewModel);
I have 3 'groups' of files a user can upload to.
(I will do the actual upload functionality later)
I'm struggling with how to bind my row to a specific item of the array?
Maybe I shouldn't use an observable array?
<div class="row files" id="files1" data-bind="???">
<h2>Files 1</h2>
<span class="btn btn-default btn-file">
Browse <input data-bind="event: {change: fileUpload}" type="file" />
</span>
<br />
<div class="fileList" data-bind="foreach: files"> <span data-bind="text: name"></span>
Remove
</div>
</div>
The idea is when a user selects files, they appear in a list under the button:
..with a link to remove the file from the upload queue.
I've set up a fiddle here - https://jsfiddle.net/alexjamesbrown/c9fvzjte/
There are few important modifications required to make your code work independently across files 0,1,2
KeyNote
event: { change: function(){fileUpload($data,$element.files[0])}}
here we are passing our selected file i.e filedata using $element in
change event not in usual click event . Filedata will have complete file information .
view:
<div class="row files" id="files1" data-bind="foreach:fileGroups">
<h2>Files 0</h2>
<span class="btn btn-default btn-file">
Browse <input data-bind="event: { change: function() { fileUpload($data,$element.files[0]) } }" type="file" />
</span>
<div class="fileList" data-bind="foreach: files"> <span data-bind="text: name"></span>
Remove
</div>
viewModel:
var SubFunction = function (data) {
var self = this;
self.name = data.name;
self.removeFile = function (item1) {
item1.files.remove(this); //current reference data & item1 has parent reference data
}
}
var FileGroupViewModel = function (id) {
var self = this;
self.id = ko.observable(id);
self.files = ko.observableArray([new SubFunction({
'name': 'Test'
})]);
self.fileUpload = function (item1, item2) {
self.files.push(new SubFunction(item2));
};
}
var ViewModel = function () {
var self = this;
self.fileGroups = ko.observableArray();
self.getFileGroupById = function (id) {
ko.utils.arrayFilter(self.fileGroups(), function (item) {
return item.id == id;
});
};
self.uploadFiles = function () {
alert('Uploading');
}
}
var viewModel = new ViewModel();
viewModel.fileGroups.push(new FileGroupViewModel(1));
ko.applyBindings(viewModel);
working sample up for grabs here
Working sample if you are planning to reuse your Html
I would like to include an inner element in an outer element using knockout, is this in any way possible?
HTML:
<div id='id1'>
<span data-bind="text: outerText()"></span>
<div id='id2'>
<span data-bind="text: innerText()"></span>
</div>
</div>
JavaScript:
var outerModel = function() {
this.outerText = ko.observable("Outer Model - Outer Text");
};
ko.applyBindings(new outerModel(), document.getElementById('id1'));
var innerModel = function() {
this.innerText = ko.observable("Inner Model - Inner Text");
};
ko.applyBindings(new innerModel(), document.getElementById('id2'));
This gives me an error:
ReferenceError: Unable to process binding "text: function(){return innerText() }"
Message: 'innerText' is undefined
I understand the error as the outer model doesn't contain the innertext and therefore the thing crashes.
My questions is if there is a proper/better/correct way of having an inner element and getting it to work in knockout.
Note: I do not want the innerModel to be a member/child of the outerModel as they are just in this HTML layout for layout purposes but aren't necessarily related.
Any help appreciated.
Thanks
Usually your best bet there is to make the inner stuff a property of your outer stuff and then just bind normally (possibly with with). E.g.:
var innerModel = function() {
this.innerText = ko.observable("Inner Model - Inner Text");
};
var outerModel = function() {
this.outerText = ko.observable("Outer Model - Outer Text");
this.inner = ko.observable(new innerModel());
};
ko.applyBindings(new outerModel(), document.getElementById('id1'));
...and then:
<div id='id1'>
<span data-bind="text: outerText()"></span>
<div id='id2' data-bind="with: inner">
<span data-bind="text: innerText()"></span>
</div>
</div>
Example:
var innerModel = function() {
this.innerText = ko.observable("Inner Model - Inner Text");
};
var outerModel = function() {
this.outerText = ko.observable("Outer Model - Outer Text");
this.inner = ko.observable(new innerModel());
};
ko.applyBindings(new outerModel(), document.getElementById('id1'));
<div id='id1'>
<span data-bind="text: outerText()"></span>
<div id='id2' data-bind="with: inner">
<span data-bind="text: innerText()"></span>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
But in cases where that's not possible, you can add a new binding to KO that says "don't bind within this element" as described here:
ko.bindingHandlers.stopBinding = {
init: function () {
return { controlsDescendantBindings: true };
}
};
Usage:
<div id='id1'>
<span data-bind="text: outerText()"></span>
<div data-bind="stopBinding: true">
<div id='id2'>
<span data-bind="text: innerText()"></span>
</div>
</div>
</div>
...and then do the two applyBindings in your question. (Note that I added a div around your id2 div. If you want to use a "virtual element" instead, add this line after the binding handler:
ko.virtualElements.allowedBindings.stopBinding = true;
...to enable using it with virtual elements.)
Example:
// Somewhere where you'll only do it once
ko.bindingHandlers.stopBinding = {
init: function () {
return { controlsDescendantBindings: true };
}
};
// Then:
var outerModel = function() {
this.outerText = ko.observable("Outer Model - Outer Text");
};
var innerModel = function() {
this.innerText = ko.observable("Inner Model - Inner Text");
};
ko.applyBindings(new outerModel(), document.getElementById('id1'));
ko.applyBindings(new innerModel(), document.getElementById('id2'));
<div id='id1'>
<span data-bind="text: outerText()"></span>
<div data-bind="stopBinding: true">
<div id='id2'>
<span data-bind="text: innerText()"></span>
</div>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
I'm new to Knockout and I'm building an app that's effectively a large-scale calculator. So far I have two instances of knockout running on one page. One instance is working perfectly fine, however the other one is entirely broken and just won't seem to register at all?
Below is my Javascript, fetchYear is the function that works perfectly fine and fetchPopulation is the one that's completely broken. It doesn't seem to register "ageview" from the HTML at all and I can't figure out.
The error:
Uncaught ReferenceError: Unable to process binding "foreach: function
(){return ageView }" Message: ageView is not defined
Thanks in advance.
JS:
var index = {
fetchYear: function () {
Item = function(year){
var self = this;
self.year = ko.observable(year || '');
self.chosenYear = ko.observable('');
self.horizon = ko.computed(function(){
if(self.chosenYear() == '' || self.chosenYear().horizon == undefined)
return [];
return self.chosenYear().horizon;
});
};
YearViewModel = function(yeardata) {
var self = this;
self.yearSelect = yeardata;
self.yearView = ko.observableArray([ new Item() ]);
self.add = function(){
self.yearView.push(new Item("New"));
};
};
ko.applyBindings(new YearViewModel(yearData));
},
fetchPopulation: function () {
popItem = function(age){
var self = this;
self.age = ko.observable(age || '');
self.chosenAge = ko.observable('');
self.population = ko.computed(function(){
if(self.chosenAge() == '' || self.chosenAge().population == undefined)
return [];
return self.chosenAge().population;
});
};
PopulationViewModel = function(populationdata) {
var self = this;
self.ageSelect = populationdata;
self.ageView = ko.observableArray([ new popItem() ]);
self.add = function(){
self.ageView.push(new popItem("New"));
};
};
ko.applyBindings(new PopulationViewModel(populationData));
}
}
index.fetchYear();
index.fetchPopulation();
HTML:
<div class="row" data-bind="foreach: yearView">
<div class="grid_6">
<img src="assets/img/index/calendar.png" width="120" height="120" />
<select class="s-year input-setting" data-bind="options: $parent.yearSelect, optionsText: 'year', value: chosenYear"></select>
<label for="s-year">Start year for the model analysis</label>
</div>
<div class="grid_6">
<img src="assets/img/index/clock.png" width="120" height="120" />
<select class="s-horizon input-setting" data-bind="options: horizon, value: horizon"></select>
<label for="s-horizon">Analysis time horizon</label>
</div>
</div>
<div class="row" data-bind="foreach: ageView">
<div class="grid_6">
<img src="assets/img/index/calendar.png" width="120" height="120" />
<select class="s-year input-setting" data-bind="options: ageSelect, optionsText: 'age', value: chosenAge"></select>
<label for="s-agegroup">Age group of <br> target population</label>
</div>
<div class="grid_6">
<img src="assets/img/index/clock.png" width="120" height="120" />
<input class="s-population input-setting"></input>
<label for="s-population">Size of your patient <br> population <strong>National</strong> </label>
</div>
</div>
When you do this (in fetchYear):
ko.applyBindings(new YearViewModel(yearData));
You are binding the entire page with the YearViewModel view model. But the YearViewModel doesn't have a property called ageView so you get the error and knockout stops trying to bind anything else.
What you need to do is restrict your bindings to cover only part of the dom by passing the element you want to ko.applyBindings. For example:
<div class="row" id="yearVM" data-bind="foreach: yearView">
//....
<div class="row" id="popVM" data-bind="foreach: ageView">
And then:
ko.applyBindings(new YearViewModel(yearData), document.getElementById("yearVM"));
//...
ko.applyBindings(new PopulationViewModel(populationData), document.getElementById("popVM"));
Now your bindings are restricted just to the part of the DOM that actually displays stuff from that model.
Another alternative is to just have your two view models as part of a parent view model and then you can apply the binding to the entire page. This makes it easier if you need to mix parts from both VMs and they are not conveniently separated in distinct sections of your page. Something like:
var myParentVM = {
yearVM : index.fetchYear(), // note, make this return the VM instead of binding it
popVM : index.fetchPopulation(), // ditto
}
ko.applyBindings(myParentVM);
And then you'd declare your bindings like so:
<div class="row" data-bind="foreach: yearVM.yearView">
The main reason why this is not working is because you call ko.applyBindings() more than once on a page (that is not really forbidden but is a bad practice in my opinion).
If you need to call it twice, you must call it with a container for which region this bind is meant to.
Something like this:
ko.applyBindings(new YearViewModel(yearData), document.getElementById('YourYearViewElementId'));
The error you get is from the first binding, which tries to process the whole page and does not find the 'ageView' in its ViewModel.
Better would be if you build a single ViewModel for a single Page where you have sub-models for sections if needed.
Some pseudo code for such a scenario:
var Section1ViewModel = function() {
var self = this;
self.property1 = ko.observable();
self.myComputed = ko.computed(function () {
// do some fancy stuff
});
self.myFunc = function() {
// do some more fancy stuff
};
}
var Section2ViewModel = function() {
var self = this;
self.property1 = ko.observable();
self.myComputed = ko.computed(function () {
// do some fancy stuff
});
self.myFunc = function() {
// do some more fancy stuff
};
}
var PageViewModel = function() {
var self = this;
self.section1 = ko.observable(new Section1ViewModel());
self.section2 = ko.observable(new Section2ViewModel());
self.myGlobalFunc = function() {
// do some even more fancy stuff
}
}
ko.applyBindings(new PageViewModel());
Hope that helps.
Best regards,
Chris
I am trying refresh a small widget with knockout and the mapping plugin. Here is my code so far:
var AppViewModel = function (data, total, qty) {
var self = this;
self.Products = ko.mapping.fromJS(data, {}, this);
self.CartTotals = ko.observable(total);
self.TotalQty = ko.observable(qty);
};
var func = function(u) {
return $.ajax({ type: "POST", contentType: "application/json; charset=utf-8", data: "{}", dataType: "json", url: u });
},
getQtyTotal = function(b) {
var a = 0;
$.each(b.Table.Rows, function(c) {
a += parseInt(b.Table.Rows[c].Quantity) || 0;
});
return a;
};
$.when(func("/store/MiniCart.aspx/GetShoppingCartInfo"), func("/store/MiniCart.aspx/GetCartTotal")).done(function (jsonA, jsonB) {
var ds = $.parseJSON(jsonA[0].d), ds2 = $.parseJSON(jsonB[0].d), qtyTotal = getQtyTotal(ds);
ko.applyBindings(new AppViewModel(ds, ds2, qtyTotal));
});
<div class="cartDropDownProductItemWrapper" data-bind="foreach: Products.Table.Rows">
<div class="cartDropDownProductItem">
<div class="cartDropDownProductImg">
<img id="cart_details_rpt_prod_image_0" style="height: 71px; width: 55px;" data-bind="attr: { src: ProductImageURL }">
</div>
<div class="cartDropDownProductDesc">
<h6><a data-bind="text: ModelName, attr: { href: ProductLink }"></a></h6>
<div class="cartDropDownProductDescInner">
<div class="cartDropDownColor"> COLOR
<strong><span data-bind="text:ColorName"></span></strong>
</div>
<div class="cartDropDownSize"> SIZE
<strong><span data-bind="text: SizeName"></span></strong>
</div>
<div class="cartDropDownSize"> QTY
<strong><span data-bind="text: Quantity"></span></strong>
</div>
<div class="cartDropDownPrice"> PRICE
<strong><span data-bind="text: UnitCost().toFixed(2)"></span></strong>
</div>
<div class="cartDropDownRemove">
<a href="javascript:void(0);" class="remove" onclick="removeItem('v3BuhngpE4c=')">
<img src="/images/layout/icons/remove.gif" alt="Remove Item">
</a>
</div>
</div>
</div>
<div class="clear"></div>
</div>
<!-- end fo reach -->
<div class="clear"></div>
<div class="cartDropDownButtons clearfix">
<ul class="clearfix">
<li class="countItems"><span data-bind="text: TotalQty"></span> Items</li>
<li class="subTotal" id="subTotal">SUBTOTAL: $<span data-bind="text: CartTotals().toFixed(2)"></span></li>
</ul>
</div>
It renders fine intially but when I try to rebind on a jQuery click event and call:
ko.applyBindings(new AppViewModel(ds, ds2, qtyTotal));
It duplicates the data.
If you start off by creating an empty viewModel... which takes no arguments via it's constructor, like so:
function ViewModel()
{
var self = this;
}
var viewModel = new ViewModel();
...Then you can reference it by name to load in your data using ko.mapping, like this:
ko.mapping.fromJS({ "PropertyName": plainJsObject }, {}, viewModel);
What this does is runs the ko.mapping magic on plainJsObject, and stuffs the result into a property (in this case, called PropertyName) on your viewModel object.
The part you would particularly care about is this:
If you want to refresh the data located in the viewModel.PropertyName with fresh data from the server... you just call the exact same method, and it updates that same property on your viewModel. Just call this same thing again, with new values in your plainJsObject:
ko.mapping.fromJS({ "PropertyName": plainJsObject }, {}, viewModel);
Since you have (I assume) already performed your ko.applyBindings() at some point in your code, this line above will immediately update your view.
You should only need to perform the binding once so just keep that outside of any event driven function calls.
ko.applyBindings(new AppViewModel(ds, ds2, qtyTotal));
I think you are over complicating your code in the above example. I would just return set of Product objects which contains properties for description, unit price, quantity etc and then to calculate the total, use a ko computed variable which will update automatically if a user tries to increase/decrease quantity.
function Product(item) {
var self = this;
self.description = ko.observable(item.description);
self.quantity = ko.observable(item.quantity);
self.unitPrice = ko.observable(item.unitPrice);
self.total = ko.computed(function() {
return self.quantity() * self.unitPrice();
});
}
function AppViewModel() {
var self = this;
self.products = ko.observableArray([]);
self.overvallTotal = ko.computed(function() {
var total = 0;
for (var i = 0; i < self.products().length; i++) {
total += self.products()[i].total;
}
return total;
});
self.removeProduct = function(item) { self.products.remove(item) };
// Load initial state from server, convert it to Product instances
$.getJSON("/store/MiniCart.aspx/GetShoppingCartInfo", function(allData) {
var mappedProducts = $.map(allData, function(item) { return new Product(item) });
self.products(mappedProducts);
});
}
ko.applyBindings(new AppViewModel());
You will have to adjust your server side json results to work with this.