Knockout JS binnding the ajax result - javascript

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.

Related

Knockout observablearray of observables with arrayMap function

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());

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
});

Issue with code inner variable

I have a code like the one stated below, please how do I get the value for (getData), using a code like:
var instanceArray = myGraph.getInstances(component)
I was thinking myGraph.getInstances(component).getData will do it, but it failed
this.getInstances = function(component) {
var getData = {};
$.ajax({
url: "/rpc/alerts2/commonObj_rpc.cfc?method=getInstances",
data: {"component":component},
type: "POST",
async: true,
success: function(data) {
getData = $.parseJSON(data);
console.log("hey");
var $render_component_instance = $("#instances").empty();
$("#instances").append($("<option />").val("all").text("All Instances (Summed)"));
$.each(getData, function (cIndex, cItem){
var $instance = $("<option />").val(cItem.si_instance).text(cItem.si_label.toUpperCase());
$render_component_instance.append($instance);
})
$("#instances").multiselect("refresh");
}
});
};`
You can't, the get is asynchronous. getInstances returns before the GET completes, so it's impossible for getInstances to return the data. (See further note below.)
You have (at least) three options:
Use a callback
Return a blank object that will get populated later, and have the code that needs it poll it periodically
Use a synchronous get (not a good idea)
1. Use a callback
What you can do instead is accept a callback, and then call it when the data arrives:
this.getInstances = function(component, callback) {
$.ajax({
url: "/rpc/alerts2/commonObj_rpc.cfc?method=getInstances",
data: {"component":component},
type: "POST",
async: true,
success: function(data) {
var getData = $.parseJSON(data);
console.log("hey");
var $render_component_instance = $("#instances").empty();
$("#instances").append($("<option />").val("all").text("All Instances (Summed)"));
$.each(getData, function (cIndex, cItem){
var $instance = $("<option />").val(cItem.si_instance).text(cItem.si_label.toUpperCase());
$render_component_instance.append($instance);
})
$("#instances").multiselect("refresh");
callback(getData);
}
});
};
And call it like this:
myGraph.getInstances(component, function(data) {
// Use the data here
});
2. Return a blank object that will get populated later
Alternately, you can return an object which will be blank to start with, but which you'll add the data to as a property later. This may be closest to what you were looking for, from your comments below. Basically, there's no way to access a function's local variables from outside the function, but you can return an object and then add a property to it later.
this.getInstances = function(component) {
var obj = {};
$.ajax({
url: "/rpc/alerts2/commonObj_rpc.cfc?method=getInstances",
data: {"component":component},
type: "POST",
async: false, // <==== Note the change
success: function(data) {
var getData = $.parseJSON(data);
console.log("hey");
var $render_component_instance = $("#instances").empty();
$("#instances").append($("<option />").val("all").text("All Instances (Summed)"));
$.each(getData, function (cIndex, cItem){
var $instance = $("<option />").val(cItem.si_instance).text(cItem.si_label.toUpperCase());
$render_component_instance.append($instance);
})
$("#instances").multiselect("refresh");
// Make the data available on the object
obj.getData = getData;
}
});
return obj; // Will be empty when we return it
};
And call it like this:
var obj = myGraph.getInstances(component);
// ...later...
if (obj.getData) {
// We have the data, use it
}
else {
// We still don't have the data
}
3. Use a synchronous get
I do not recommend this, but you could make the call synchronous. Note that synchronous ajax requests will go away in a future version of jQuery. But just for completeness:
this.getInstances = function(component) {
var getData;
$.ajax({
url: "/rpc/alerts2/commonObj_rpc.cfc?method=getInstances",
data: {"component":component},
type: "POST",
async: false, // <==== Note the change
success: function(data) {
var getData = $.parseJSON(data);
console.log("hey");
var $render_component_instance = $("#instances").empty();
$("#instances").append($("<option />").val("all").text("All Instances (Summed)"));
$.each(getData, function (cIndex, cItem){
var $instance = $("<option />").val(cItem.si_instance).text(cItem.si_label.toUpperCase());
$render_component_instance.append($instance);
})
$("#instances").multiselect("refresh");
}
});
return getData;
};
And call it like this:
var getData = myGraph.getInstances(component);
But again, I don't advocate that. Synchronous ajax calls lock up the UI of the browser, leading to a bad user experience.

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