Knockout observablearray of observables with arrayMap function - javascript

I have a problem creating observable array of observables. I search on google but didn't find a solution. It may be something simple that I can't notice, because I'm new to Knockout.
But I have the model:
eventsModel = function () {
var self = this;
self.endTimeInMinutes = ko.observable();
self.events = ko.observableArray([]);
self.startTripTime = ko.observable();
self.endTripTime = ko.observable();
}
and I want to have an observables items in my array so I write a ViewModel and bind the model.
eventItemViewModel = function(o) {
var self = this;
self.BeginInMinutes = ko.observable(o.BeginInMinutes);
self.Type = ko.observable(o.Type);
};
var events = new eventsModel();
ko.applyBindings(events);
And I'm fetching the data using AJAX:
function GetEvents() {
$.ajax({
url: "Contact.aspx/GetEvents",
async: true,
type: "POST",
contentType: "application/json",
dataType: "json",
success: function (data) {
var temp = data.d;
endTimeInMinutes = temp["EndInMinutes"];
eventsArr = temp["Events"];
eventsArray = JSON.parse(JSON.stringify(eventsArr));
events.events(eventsArray);
},
});
}
After this I have an observable array but without observables values inside. Now I trying to add in my AJAX method:
events.events(ko.utils.arrayMap(eventsArray, function(eve) {return new eventItemViewModel(eve); }));
But if I do console.log on this, then I am getting array of objects, and in each object there is a BeginInMinutes and Type, but it's value is like function d()... etc.
I'm really get stucked with it, and I believe I made some very simple mistake somewhere.
Thanks for helping me.

You already got an observableArray with observable element inside it,Your problem really is with getting those values
use this code to get the real value of the first element in the array.
console.log(events.events()[0].BeginInMinutes());

Related

Knockout JS binnding the ajax result

I am new to Knockout JS, I am trying to bind the ajax result data to Knockout JS viewmodel, but I am facing the problem while binding the data to view, I have create model and viewmodel and I am getting the result from ajax. Need help.
Below is my code:
// ajax on page load///
$.ajax({
type: "POST",
dataType: "json",
url: baseUrl + 'api/xxx/xxx',
data: UserProfileModel,
success: function(data) {
result = data;
////view model////
userDetailsViewModel(result);
},
error: function(error) {
jsonValue = jQuery.parseJSON(error.responseText);
//jError('An error has occurred while saving the new part source: ' + jsonValue, { TimeShown: 3000 });
}
});
//// view model///
var userDetailsViewModel = function(result) {
console.log(result);
self = this;
self.user = ko.observable(new userModel(result));
};
$(document).ready(function() {
ko.applyBindings(userDetailsViewModel());
});
/// Model////
function userModel(result) {
this.name = ko.observable();
this.userName = ko.observable();
}
Your userDetailsViewModel is a function that returns undefined. You'll either have to use new or create a return statement if you want it to create an actual viewmodel. E.g.:
var UserDetailsViewModel = function(result) {
var self = this;
self.user = ko.observable(new UserModel(result));
};
var mainUserDetailsViewModel = new UserDetailsViewModel(data);
You'll have to store a reference if you want to update your viewmodel from the scope of the ajax callback. Alternatively, you could make the ajax functionality part of the viewmodel. The easiest example:
var mainUserDetailsViewModel = new UserDetailsViewModel(data);
$.ajax({
success: function(data) {
mainUserDetailsViewModel.user(new UserModel(data));
}
});
Make sure you use this model in your applyBindings call:
ko.applyBindings(mainUserDetailsViewModel);
Note that I've used CapitalizedNames for functions that need to be instantiated, and uncapitalizedNames for instances of those functions. Following default naming conventions might help keeping track of what's what.

KnockoutJS Binding a list of objects from an AJAX call

I have a data-object in my Javascript code described as follows:
//Data Object that represents products
function Product(n, p, t, d) {
this.name = ko.observable(n);
this.price = ko.observable(p);
tags = typeof (t) !== 'undefined' ? t : [];
this.tags = ko.observableArray(tags);
discount = typeof (d) !== 'undefined' ? d : 0;
this.discount = ko.observable(discount);
this.formattedDiscount = ko.computed(function ()
{ return (this.discount() * 100) + "%"; }
,this);
}
Then, I have an AJAX call to retrieve data in JSON format
$(document).ready(function () {
$.ajax({
type: "POST",
url: "ShoppingCartExampleExample.aspx/SendData",
data: "{}",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (msg) {
alert(msg.d);
}
});
});
The AJAX call is working, I got this as result:
[
{"Discount":0,
"Name":"Chocolate",
"Price":"7.99"
"tags": ["Mars","Snickers"]
},
{"Discount":0.05,
"Name":"Beer",
"Price":"3.99"
"tags": ["Large","Extra"]
}
]
How could I map this list of objects received from an AJAX call to my Data-object?
I would like to map this list to an observable array and also map the tags array to an observable array, because I have a foreach binding that fills an HTML table to present the products and tags.
Thank You
Knockout utility functions are now included in the latest builds and are my preferred way of handling things. The mapping plugin is really overused at times and can make your objects more bloated than they need to be if not used correctly.
When your array of objects are returned, use the utility class:
var newProducts = ko.utils.arrayMap(data, function(dataItem) {
return new Product(...);
});
ko.utils.arrayPushAll(myViewModel.products, newProducts);
Pretty straight forward and supported. You could even put the arrayMap directly into the arrayPushAll as the second parameter and skip declaring the newProducts variable.
So assuming in your success function msg.d is your array. Just loop through the array and for each item call new Product for each item and then push the result into the observable array of whatever you parent view model is. For example, if you have something like:
var rootViewModel = function() {
this.products = ko.observableArray();
//... whatever other properties and functions you have
}
var myViewModel = new rootViewModel();
Then in your success function of your ajax call, you'd do something like:
success: function (msg) {
msg.d.forEach(function(item) {
myViewModel.products.push(new Product(item.Name, item.Price, item.tags. item.Discount));
}
}
Some of this plumbing can be avoided by using the mapping plugin which is pretty configurable to allow you to map your plain javascript objects from your ajax call to whatever view model you want.

wait for ajax result to bind knockout model

I have getGeneral function that calls ajax GET. When ajax recieves data (json), it creates KO model from given json and returns created KO.
When Knockout model is created and values are assigned, knockout applybindings should be called. Here is my code:
Defines GeneralModel and some related functions (inside "GeneralModel.js"):
var GeneralModel = function() {
//for now it is empty as data ar binded automatically from json
// CountryName is one of the properties that is returned with json
}
function getGeneral(pid) {
$.ajax({
url: "/api/general",
contentType: "text/json",
dataType: "json",
type: "GET",
data: { id: pid},
success: function (item) {
var p = new GeneralModel();
p = ko.mapping.fromJS(item);
return p;
},
error: function (data) {
}
});
}
This is called from another file (GeneralTabl.html), it should call get function and applyBindings to update UI:
var PortfolioGeneral = getGeneral("#Model.Id");
ko.applyBindings(PortfolioGeneral, document.getElementById("pv-portfolio-general-tab"));
However, in this scenario I am getting error (CountryName is not defined). This is because applyBindings happens before ajax returns data, so I am doing applyBindings to empty model with undefined properties.
Mapping from Json to Model happens here and is assignes values:
p = ko.mapping.fromJS(item);
I can also fill in GeneralModel with all fields, but it is not necessary (I guess):
var GeneralModel = function() {
CountryName = ko.observable();
...
}
It will still give an error "CountryName is not defined".
What is the solution?
1) Can I somehow move getGeneral inside GeneralModel, so get data would be part of GeneralModel initialization?
or
2) Maybe I should somehow do "wait for ajax results" and only then applyBindings?
or
I believe there are other options, I am just not so familiar with KO and pure JS.
Note: I fully understand that this is because Ajax is Async call, so the question is how to restructure this code taking into account that I have two seperate files and I need to call getGeneral from outside and it should return some variable.
Try using the returned promise interface:
function getGeneral(pid) {
return $.ajax({
url: "/api/general",
contentType: "text/json",
dataType: "json",
type: "GET",
data: {
id: pid
}
});
}
getGeneral("#Model.Id").done(function (item) {
var p = new GeneralModel();
p = ko.mapping.fromJS(item);
ko.applyBindings(p, document.getElementById("pv-portfolio-general-tab"));
}).fail(function () {
//handle error here
});

Knockout, get JSON from View Model after mapping it

I start with empty view model, then I do an ajax request for data from database. Collected data I mapping using Knockout.Mapping plugin to view model.
Like this:
var myName = new function(){
this.viewModel = {};
var getData = function () {
var mapping = {
'Members': {
create: function (options) {
return new UserMode(options.data);
}
}
}
$.ajax({
url: 'api/board',
data: $.param({"BoardId": 1}),
dataType: 'json',
success: function (data, textStatus, jqXHR) {
this.viewModel = ko.mapping.fromJS(data, mapping);
ko.applyBindings(this.viewModel);
},
});
}
};
Then I opened a JavaScript console in Chrome and typed:
ko.toJSON(myName.viewModel);
And results is
"{}"
I expected to see viewModel with data from server, not empty object.
You made a little confusion, I think you should call applybindings before doing your AJAX loading stuff.
I updated one of my old fiddle to replicate your problem, check it out, hope it will help you!
http://jsfiddle.net/ingro/Buscp/

Specifying a callback in jQuery.ajax to OpenCalais SemanticProxy service

I'm trying to query the OpenCalais service semanticproxy.com. Unfortunately, their url format is as follows:
http://service.semanticproxy.com/processurl/APIKEY/jsonp:handler_function/http://en.wikipedia.org/wiki/Germany
notice that the function callback, is not in a callback=? parameter, but rather follows the response format (jsonp:). This means that I can't use .getJSON, but rather need to use the .ajax method. So I have the following object definition:
function Subject() {
}
Subject.prototype.populate = function(page_title) {
var url = "http://service.semanticproxy.com/processurl/APIKEY/jsonp:handler/http://en.wikipedia.org/wiki/" + page_title;
$.ajax({url: url, dataType: "script", type: "GET", cache: false, callback: null, data: null});
};
var handler = function (data) {
// do stuff with the returned JSON
};
s = new Subject();
s.populate("Germany");
This works fine. But what I really want to do is set properties of my Subject object. But I don't know how to create a function in the context of the Subject that will be able to be used as the callback. i.e:
Subject.prototype.handler = function(data) { this.title = data.title }
Any ideas?
You'd have to set a function on the window object. This is essentially (I think) what jQuery does with its .getJSON method. The below is a bit hacky but hopefully it points you in the right direction:
function Subject() {
}
Subject.prototype.populate = function(page_title) {
// Save context object
var subject = this;
// Create function name like subjectHandler1281092055198
var functionName = "subjectHandler" + new Date().getTime();
window[functionName] = function(data) {
// Invoke function with saved context and parameter
subject.handler.call(subject, data);
}
var url = "http://service.semanticproxy.com/processurl/APIKEY/jsonp:" + functionName + "/http://en.wikipedia.org/wiki/" + page_title;
$.ajax({url: url, dataType: "script", type: "GET", cache: false, callback: null, data: null});
};
Subject.prototype.handler = function (data) {
// do stuff with the returned JSON
};
s = new Subject();
s.populate("Germany");
I don't think you're going to be able to do this, just because of how JSONP works, look at how it actually comes back to the browser, it pretty much does this:
<script type="text/javascript">
handler({ title: "Germany", ...other properties... });
</script>
There's no way to maintain a reference here, you could do one request at a time, or keep an object map for each subject, but there's no way to do it in the JSONP request.
An object map would look something like this:
//delcare this once for the page
var subjects = {};
//do this per subject
var s = new Subject();
s.populate("Germany");
subjects["Germany"] = s;
Then in your hanldler, if any of the data properties is "Germany", you could get it that way, for example:
var handler = function (data) {
var subject = subjects[data.title];
//subject is your Germany subject, use it, go nuts!
};

Categories

Resources