wait for ajax result to bind knockout model - javascript

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

Related

Using AJAX to get dynamic data for Materialize autocomplete

​i've been struggling with this problem for the past 3 days or so and i'm wondering if anyone could help me with this. i have a form that has a textbox using autocomplete. what i want is to have the textbox get the data from an AJAX function and not just static data.
Based on the Materialize documentation, the way to get static data is by passing an object as the second argument upon initialization of an autocomplete instance:
let elem = document.querySelector('#brand');
let instance = M.Autocomplete.init(elem, { data: { "Apple": null, "Microsoft":null} });
​
Now since i want to use AJAX for dynamic data, i made an AJAX function and also another function to pass the data from the AJAX function to after success (this is to bypass the problem with using asyncronous functions), like so:
function ajaxCall() {
$.ajax({
type: 'GET',
url: 'showBrands',
dataType: 'json',
success: passData
});
}
function passData(data) {
return data;
}
After which, i tried to do the following but no luck though.
let elem = document.querySelector('#brand');
let instance = M.Autocomplete.init(elem, { data: ajaxCall() });
Am i doing something wrong here? Or is there a better approach? Any help is greatly appreciated. thanks in advance!
Try put your materialize plugin inside passdata function:
function ajaxCall() {
$.ajax({
type: 'GET',
url: 'showBrands',
dataType: 'json',
success: passData
});
}
function passData(data1) {
let elem = document.querySelector('#brand');
var instance = M.Autocomplete.getInstance(elem);
if (instance===null || instance===undefined){
instance = M.Autocomplete.init(elem, { data: data1 });
}else{
instance.updateData(data1);
}
}

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.

How to submit a form and pass some extra parameters to a $.getJSON callback method?

I know how to pass some parameters to a JQuery $.getJSON callback method, thanks to this question:
$.getJSON('/website/json',
{
action: "read",
record: "1"
},
function(data) {
// do something
});
And I can also submit a form to a $.getJSON callback method:
$.getJSON('/website/json', $(formName)
function(data) {
// do something
});
But I want to pass some parameters AND submit some form elements. How can I combine the two things togheter?
I could serialize the form elements and manually add some parameters to the url, and it looks like it works:
$.getJSON('/website/json',
'action=read&record=1&'
+ $(formName).serialize(),
function(data) {
// do something
});
But it doesn't look very elegant. Is this the proper way, or there's a better way to do it?
We could implement the functionality demonstrated in this answer as a custom jQuery instance method which produces an object of key/value pairs from a form and combines it with the properties that aren't derived from the form:
$.fn.formObject = function (obj) {
obj = obj || {};
$.each(this.serializeArray(), function (_, kv) {
obj[kv.name] = kv.value;
});
return obj;
};
$.getJSON('/website/json', $(formName).formObject({
action: "read",
record: "1"
}), function(data) {
// do something
});
Make an Ajax post to send the data to the server. Retrieve the parameter data in the backend code along with the form data.
var formData = {data from form};
formData.action = 'read';
formData.post = '1';
$.ajax({
url: '/website/json',
type: "post",
data: formData
}).done(function (data) {
// remove prior values set upon request response
formData.action = null;
formData.post = null;
});

Set variables in JavaScript once function finishes

I have two functions that makes Ajax calls: getData and getMoreData. getMoreData requires a url variable that is dependent on the url variable getData. This questions continues from: String append from <select> to form new variable.
By appending an item obtained from the received from getData onto a base URL, I create a new variable (Let's call this NewDimensionURL) that I use for getMoreData url. However, NewDimensionURL will show error because the original list (from getData) has yet to be populated and will append nothing onto the base URL.
An idea that I have is to set NewDimensionalURL once getData finishes populating the combobox, so that getMoreData can run after.
JavaScript
var GetDimensions = 'SomeURL1';
//--Combines URL of GetDimensionValues with #dimensionName (the select ID)
var UrlBase = "Required URL of getMoreData";
var getElement = document.getElementById("dimensionName");
var GetDimensionValues = UrlBase + getElement.options[getElement.selectedIndex].text;
function handleResults(responseObj) {
$("#dimensionName").html(responseObj.DimensionListItem.map(function(item) {
return $('<option>').text(item.dimensionDisplayName)[0];
}));
}
function handleMoreResults (responseObj) {
$("#dimensionId").html(responseObj.DimensionValueListItem.map(function(item) {
return $('<option>').text(item.dimensionValueDisplayName)[0];
}));
}
function getData() {
debugger;
jQuery.ajax({
url: GetDimensions,
type: "GET",
dataType: "json",
async: false,
success: function (data) {
object = data;
handleResults(data);
}
});
}
function getMoreData() {
debugger;
jQuery.ajax({
url: GetDimensionValues,
type: "GET",
dataType: "json",
async: false,
success: function (data) {
object = data;
handleMoreResults (data);
}
});
}
Answered
Reordered as:
var GetDimensionValues;
function handleResults(responseObj) {
$("#dimensionName").html(responseObj.DimensionListItem.map(function(item) {
return $('<option>').text(item.dimensionDisplayName)[0];
}));
GetDimensionValues = UrlBase + getElement.options[getElement.selectedIndex].text;
}
Created onchange function Repopulate() for getMoreData() to parse and for handleMoreResults() to populate.
I'm guessing you just do getData(); getMoreData() back to back? If so, then you're running getmoreData BEFORE getData has ever gotten a response back from the server.
You'll have to chain the functions, so that getMoreData only gets executed when getData gets a response. e.g.
$.ajax($url, {
success: function(data) {
getMoreData(); // call this when the original ajax call gets a response.
}
});
Without seeing your code it's hard to say if this is the right solution, but you should try chaining the functions:
$.ajax({url: yourUrl}).then(function (data) {
// deal with the response, do another ajax call here
}).then(function () {
// or do something 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/

Categories

Resources