Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 years ago.
Improve this question
I am able to bind JSON data as follows:
My json file
$(document).ready(function () {
var jsondata = JSON.parse(var1);
DisplayFields = function (jsondata) {
var viewModel = {
d: ko.observableArray(jsondata),
pageSize: ko.observable(10),
pageIndex: ko.observable(0),
previousPage: function () {
this.pageIndex(this.pageIndex() - 1);
},
nextPage: function () {
this.pageIndex(this.pageIndex() + 1);
}
};
viewModel.maxPageIndex = ko.dependentObservable(function () {
return Math.ceil(this.d().length / this.pageSize()) - 1;
}, viewModel);
viewModel.pagedRows = ko.dependentObservable(function () {
var size = this.pageSize();
var start = this.pageIndex() * size;
return this.d().slice(start, start + size);
}, viewModel);
ko.applyBindings(viewModel, document.getElementById("Datasection"));
ko.applyBindings(viewModel, document.getElementById("prevnext"));
};
DisplayFields(jsondata);
});
My HTML file
<section class="col-lg-12 paddingBottom40 paddingTop20 RecentInnovation" id="Datasection" data-bind='template: { foreach: pagedRows }'>
<div class="row">
<section class="col-lg-1 col-md-1 col-sm-1 col-xs-4">
<div class="bgOrange blue text-center paddingTop10 paddingBottom10">
<span class="size18" data-bind="text: Views"></span>
<br>
View
</div>
</section>
<section class="col-lg-9 col-md-9 col-sm-9 col-xs-12">
<a data-bind="attr: { href: '../SitePages/IdeaDetails.aspx?ideaid=' + ID }" class="size14 green"><strong><span data-bind=" text: BusinessProblem"></span></strong></a>
<br>
<p class="paddingTop5">Category:<span data-bind="text: InnovationType" class="green"></span> Submitted by: <span data-bind=" text: Requester" class="green"></span> On <span data-bind=" text: Created " class="green"></span></p>
<p class="paddingTop5">Tags: <span data-bind="text: Keywords" class="green"></span>   Domain: <span data-bind=" text: Domain" class="green"></span>   SubDomain: <span data-bind=" text: SubDomain" class="green"></span></p>
</section>
<section class="col-lg-2 col-md-2 col-sm-2 col-xs-12 text-right"><span data-bind="text: Status"></span><span data-bind=" css: statusCss"></span></section>
</div>
</section>
I want to add pagination(1,2,3,4...10,11) into my page with pages link so that user can go straight to any page. How to modify above javascript code to achieve this.
Thanks
HTML:
<span data-bind="with: previousLink">
<a data-bind="attr: { href: href }, click: $parent.sendPrevPage" title="Previous Page">Previous Page...</a>
</span>
<span data-bind="with: nextLink">
<a data-bind="attr: { href: href }, click: $parent.sendNextPage" title="Next Page">Next Page...</a>
</span>
JS:
function LinkViewModel(model) {
model = model || {};
var self = this;
self.href = model.Href || ' ';
self.rel = model.Rel || ' ';
}
executeLink = function (linkVm) {
$.ajax({
url: linkVm.href,
type: "GET",
success: function (response) {
//do stuff
},
error: function (xhr, ajaxOptions, thrownError) {
//do stuff
}
});
}
self.sendPrevPage = function () {
executeLink(self.previousLink());
};
self.sendNextPage = function () {
executeLink(self.nextLink());
};
Here's a super simplified example:
HTML:
<div data-bind="foreach: pageNumbers">
<a data-bind="click: $root.gotoPage, text: pageNumber"></a>
</div>
Javascript:
var viewModel = function(){
var self = this;
self.maxPageIndex = ko.observable(10);
self.pageNumbers = ko.computed(function() {
var pages = [];
for (i=1; i<=self.maxPageIndex(); i++){
pages.push({pageNumber: i});
}
return pages;
});
self.gotoPage = function(page){
console.log("Go to page " + page.pageNumber);
};
};
ko.applyBindings(new viewModel());
Note that knockout passes in the scoped object (in this case the 'page') into the click function. You don't need to wire anything up, just call the click function from the $root and it will pass in the object from the scope of the for loop... I think that's cool, but I'm a dork that way.
Fiddle:
http://jsfiddle.net/brettwgreen/jp6hu5ho/
Related
There's a paragraph in knockout docs that said you can create an observableArray with properties as observables but there isn't an example of that:
http://knockoutjs.com/documentation/observableArrays.html
So what I'm trying to achieve is to add an element to an observableArray that has an observable property to detect state changes when it's clicked. So here is my code what I have so far
export class Team {
Name: KnockoutObservable<string>;
Score: KnockoutObservable<number>;
ListTeamsArray: KnockoutObservableArray<any>;
selectedTeam: KnockoutObservable<boolean>;
constructor(json) {
var self = this;
if (json !== null) {
self.Name = ko.observable(json.Name);
self.Score = ko.observable(0);
self.ListTeamsArray = ko.observableArray();
self.selectedTeam = ko.observable(false);
}
}
addTeam = () => {
var self = this;
//Working correctly and I'm declaring "selectedTeam" as an observable with initial value of "false"
var newTeam = { Name: self.Name(), Score: 0, selectedTeam: ko.observable(false)};
self.ListTeamsArray.push(newTeam);
}
//Here I create a function that is passing a "team" parameter (the one in the array and it's working fine
teamSelectedFn = (team: Team, event) => {
var self = this;
$(".teamPanel").css("background", "none");
//Loop thru array in knockout to assign selected value, probably there's a better way
ko.utils.arrayForEach(self.ListTeamsArray(), (item) => {
if (item.Name === team.Name) {
$(event.currentTarget).css("background", "#a4e4ce");
item.selectedTeam = ko.observable(true);
} else {
item.selectedTeam = ko.observable(false);
}
});
//just for testing
ko.utils.arrayForEach(self.ListTeamsArray(), (item) => {
console.log(item);
console.log(item.selectedTeam());
});
}
}
And this is the HTML
<div class="row" id="teamCrud">
<div class="col-sm-3" >
<div class="form-group">
<input class="form-control" data-bind="value: Name" />
#*<span data-bind="text: Score"></span>*#
<button data-bind="click: addTeam" class="btn btn-success">Add</button>
</div>
</div>
<div class="col-sm-9">
Equipos
<div data-bind="foreach: ListTeamsArray" class="row">
<div class="col-sm-3">
<div class="panel panel-default teamPanel" data-bind="click: $parent.teamSelectedFn, style: { border: selectedTeam() ? '2px solid red' : 'none'}#*, style: { background: $data.selectedTeam() ? 'red' : 'none'}*#">
<div class="panel-heading" data-bind="text: Name"></div>
<div class="panel-body">
Score:
<p data-bind="text: Score"></p>
Seleccino
<p data-bind="text: selectedTeam()"></p>
</div>
</div>
</div>
</div>
</div>
</div>
Everything it's working, I know I can change the background color of the HTML element with knockout but I need to detect the dependency change. It's not detecting the changes from the observable inside the array. Is there something else I need to do or I'm handling this the wrong way?
In your click function you are overwriting the bound observable with a new observable. You probably just need to change your function to update the existing observable instead of replacing it.
teamSelectedFn = (team: Team, event) => {
var self = this;
$(".teamPanel").css("background", "none");
//Loop thru array in knockout to assign selected value, probably there's a better way
ko.utils.arrayForEach(self.ListTeamsArray(), (item) => {
if (item.Name === team.Name) {
$(event.currentTarget).css("background", "#a4e4ce");
item.selectedTeam(true);
} else {
item.selectedTeam(false);
}
});
I am not understanding why the function goToProfile(otherUserId) is 'not defined' only some of the times.
For example, on the page, I have a link to a user that works fine by using the '.userProfileLink' click event.
<%= link_to image_tag(user.avatar.url(:medium)), "/profile/#{user.id}/view", class: "userProfileLink", :"value" => user.id, remote: true %>
Then I click #requestMeeting which renders this handlebars template:
<script id="request-meeting-form" type="text/x-handlebars-template">
<form id="requestMeetingForm">
<div class="form_section">
<h4 style="text-align:center;">Request 1 Hour Meeting</h4>
<br>
<div class="wrapper_input col_8_of_16">
<h4 class="label_standard">Date</h4>
<input type="text" id="meeting_date" class="input_standard datePicker" onfocus="this.blur()"></input>
<input type="hidden" id="alt_meeting_date" class="input_standard datePicker"></input>
</div>
<div class="wrapper_input col_4_of_16">
<h4 class="label_standard">Start Time</h4>
<input type="text" id="meeting_time" class="input_standard time_picker" onfocus="this.blur()"></input>
</div>
</div>
<div class="form_section">
<div class="wrapper_input">
<use xlink:href="#map"/></use></svg>
</div>
<br>
<div class="wrapper_input col_8_of_16">
<input type="text" name="location" id="locationField" placeholder="Location Name" class="input_standard" ></input>
</div>{{!--
--}}<div class="wrapper_input col_8_of_16">
<input type="text" name="location_address" id="addressField" placeholder="Location Address" class="input_standard"></input>
</div>
<input type="hidden" id="currentUser"></input>
</div>
<div id="mapLocation">
</div>
**************** IMPORTANT PART *********************
<div class="form_section submit_cancel">
<div class="wrapper_input cancel" >
<use xlink:href="#circleClose"/></svg>
</div>
********************************************
<div class="wrapper_input submit">
<div class="actions">
<button type="submit" class="btn_primary" id="requestMeetingButton" >Request Meeting <svg class="" ><use xlink:href="#sendPlane"/></svg></button>
</div>
</div>
</div>
</form>
</script>
When I try to call goToProfile again in the template above, I get an error that goToProfile is not defined.
application.js:
$(document).on('click', '#goToProfile', function(e) {
e.preventDefault();
var value = $(this).attr('value')
var otherUserId = parseInt(value);
$('#profileSection').empty();
goToProfile(otherUserId);
});
var ready;
ready = function(){
var currentUserId;
getCurrentUser().done(function() {
var currentUserId = currentUserId
});
$('.userProfileLink').click(function(e) {
var value = $(this).attr('value')
otherUserId = value;
goToProfile(otherUserId);
});
var profileSource = $("#user-profile").html();
var compiledProfile = Handlebars.compile(profileSource);
var languageSource = $("#languages").html();
var compiledLanguage = Handlebars.compile(languageSource);
var language = ""
var currentUserId;
var goToProfile = function(otherUserId) {
$.ajax({
url: '/get_user',
type: 'GET',
data: {otherUserId: otherUserId},
success: function(user) {
var profileInfo;
getUserImage(otherUserId).done(function() {
var profileInfo = {
first_name: user.first_name,
location: user.location,
bio: user.bio,
nationality: user.nationality,
userId: user.id,
userImage: userImage,
};
var profileTemplate = compiledProfile(profileInfo);
$('.yieldedInfo').empty();
$('.yieldedInfo').append('<div class="profileContainer">' + profileTemplate + '</div>');
});
getLanguagesUsers(user.id);
$('#requestMeeting').click(function(e) {
e.preventDefault();
requestMeetingForm(this.value);
});
},
error: function(err) {
console.log(err);
}
});
};
var getLanguagesUsers = function(userId) {
$.ajax({
url: '/user_languages',
type: 'GET',
data: {userId: userId},
success: function(languages) {
for(var i=0; i<languages.length; i++) {
if(languages[i].level != 5) {
var id = languages[i].language_id
var langUrl = '/languages/'+id;
$.getJSON(langUrl, function(lang) {
var language = lang.language
var languageInfo = {
language: language
};
var languageTemplate = compiledLanguage(languageInfo);
$('#learningLanguages').append(languageTemplate);
});
} else {
var id = languages[i].language_id;
var langUrl = '/languages/'+id;
$.getJSON(langUrl, function(lang) {
var language = lang.language
var languageInfo = {
language: language
};
var languageTemplate = compiledLanguage(languageInfo);
$('#fluentLanguages').append(languageTemplate);
});
};
};
},
error: function(err) {
console.log(err);
}
});
};
};
$(document).ready(ready);
$(document).on('page:load', ready);
How can I make goToProfile() available to be called all the time?
Thanks!
You can do what you need by binding the click event handler at the same scope level as the function it needs to call...
var ready = function(){
$(document).on('click', '#goToProfile', function(e) {
e.preventDefault();
var value = $(this).attr('value')
var otherUserId = parseInt(value);
$('#profileSection').empty();
goToProfile(otherUserId);
});
// the rest of your ready function goes here
};
If you do it that way then the event handler has access to the goToProfile function as they're both within the ready function. Nothing outside that function can access the private methods inside it.
I have an array doorsForSitewhere each item will have a Doors Array and each door will have a Schedules array.
It looks like :
var scheduleList = new[]
{
new { ScheduleId = "Schedule1",ScheduleName = "Always On" },
new { ScheduleId = "Schedule2",ScheduleName = "Never On"}
};
var doorsForSite = new[]
{
new { ControllerId ="controller1",ControllerName="Eagle",IsChecked = "false",
Doors = new[]
{
new { DoorId="Door1",DoorName="DoorOne",Schedules = scheduleList},
new { DoorId = "Door2", DoorName = "DoorTwo",Schedules = scheduleList}
}
},
new { ControllerId ="controller2",ControllerName="NetAxis",IsChecked = "false",
Doors = new[]
{
new { DoorId= "Door3",DoorName="DoorThree",Schedules = scheduleList},
new { DoorId = "Door4", DoorName = "DoorFour",Schedules = scheduleList},
new { DoorId = "Door5", DoorName = "DoorFive",Schedules = scheduleList}
}
}
};
Now in UI ..
<ul class="accgrouptableaccordian scroll-x scroll-y">
<!-- ko foreach: $data.AddModel.ControllerDoors -->
<li>
<div class="panel-group">
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">
<span>
<span>
<span class="ispicon ispicon_accordian_droparrow">
</span>
<span class="title" style="line-height:20px;" data-bind="text: ControllerName() + ' - ' + Doors().length + ' Doors'">
</span>
</span>
<span>
</span>
</span>
</h4>
</div>
<div class="panel-collapse">
<div class="panel-body">
<div class="table-responsive panel-body">
<table class="table">
<tbody data-bind="foreach:Doors">
<tr>
<td>
<div>
<span data-bind="text:DoorId"></span>
</div>
</td>
<td class="col-md-4">
<select name="drpSchedulesAccess" class="form-control drpSchedulesAccess" data-bind="options:$data.Schedules,
optionsText: 'ScheduleName',
optionsValue: 'ScheduleId',
value: $data.ScheduleId"></select>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</li>
<!-- /ko -->
</ul>
But in Viewmodel I only want to get the values of the checked Door's selected Schedule in the dropdown.
But that's not happening.
I did
ko.utils.arrayForEach(this.AddModel.ControllerDoors(), function (item) {
ko.utils.arrayForEach(item.Doors(), function (item) {
doorsSelected.push(item);
});
});
var doors = ko.utils.arrayFilter(doorsSelected, function (item) {
return item.IsChecked == true;
});
var doorIds = ko.utils.arrayMap(doors, function (door) {
if (jQuery.inArray(door.DoorId, doorIds) == -1) {
return door.DoorId;
}
});
ko.utils.arrayForEach(doors, function (item) {
debugger;
ko.utils.arrayForEach(item.Schedules, function (item) {
$('.drpSchedulesAccess option:selected').each(function (i) {
schedulesSelected.push($(this).val());
});
});
});
and I checked 3 doors with 3 selected Schedule from dropdown.
But I am getting a schedule array length of 30.
Why is it so ?
you might need to slightly tweek your code & most importantly do everthing is knockout-ish way .
viewModel:
var ViewModel = function() {
var self = this;
self.ControllerDoors = ko.observableArray(ko.mapping.fromJS(doorsForSite)()); // mapping to convert everything to observable
self.check = function() {
var doorsSelected = [];
ko.utils.arrayForEach(self.ControllerDoors(), function(item) {
//you can add condition here based on controllerName IsChecked & reduce looping
ko.utils.arrayForEach(item.Doors(), function(item) {
if (item.IsChecked())
doorsSelected.push(item);
});
});
console.log(doorsSelected);
}
};
catch the working demo here and check console window to find Results .
Things to note :
() : used to read observable (you can find it's usage on current view)
The way you look up the selected options in your view is not "the knockout way". In these lines:
$('.drpSchedulesAccess option:selected').each(function (i) {
schedulesSelected.push($(this).val());
});
you're using the DOM to store your ViewModel state, which isn't bad per se, but not how knockout works.
When using knockout, your ViewModel should contain all data and communicate with the DOM through data-binds.
You haven't shown us a snippet of working code, so I'll try to recreate parts of your UI to show how it should work.
var DoorViewModel = function(schedules, door) {
return Object.assign({
checked: ko.observable(true),
selectedSchedule: ko.observable(),
schedules: schedules
}, door);
};
var ViewModel = function(doors, schedules) {
this.doors = ko.observableArray(doors.map(DoorViewModel.bind(null, schedules)));
this.selectedScheduleIds = ko.computed(function() {
return this.doors()
.filter(function(door) { return door.checked(); })
.map(function(door) {
return door.selectedSchedule()
? door.selectedSchedule().ScheduleId
: null;
});
}, this);
};
var schedules = [{
'ScheduleId': "Schedule1",
'ScheduleName': "Always On"
}, {
'ScheduleId': "Schedule2",
'ScheduleName': "Never On"
}];
var doors = [{
'DoorId': "Door1",
'DoorName': "DoorOne"
}, {
'DoorId': "Door2",
'DoorName': "DoorTwo"
}];
ko.applyBindings(new ViewModel(doors, schedules));
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<ul data-bind="foreach: doors">
<li>
<input type="checkbox" data-bind="checked: checked">
<span data-bind="text: DoorName"></span>
<select data-bind="options: schedules,
value: selectedSchedule,
optionsText: 'ScheduleName'"></select>
</li>
</ul>
<pre data-bind="text: JSON.stringify(selectedScheduleIds(), null, 2)"></pre>
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 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.