Knockout mapping data from ajax post vs Static data - javascript

I've encounted a strange problem with Ko mapping.
I use this piece of code:
var PList = [{ "Region": { "RegionName": "SomeRegion" }, "CDetails": {}, "Category": { "Name": "SomeCategory" }, "PSource": 1, "PDate": "0001-01-01T00:00:00"}];
var PViewModel = ko.mapping.fromJS(search('someSearch', 'True'));
var PViewModel2 = ko.mapping.fromJS(PostList);
function search(queryString, isFirst) {
$.ajax({
type: 'POST',
url: 'url',
data: { 'searchQuery': queryString },
dataType: 'json',
success: function (dt) {
if (isFirst != 'True') {
ko.mapping.fromJS(dt, PostsViewModel);
}
return dt;
}
});
};
Strangely I see 2 outcomes:
When I go to PViewModel (the one populated by ajax) I see it as undefined
When I go to PViewModel2 (the one with static data) I can see the objects as expected
*The static data of PViewModel2 is just a copy of the data returned by the ajax post.
My questions are:
Does anyone know why is this so? And how to fix it?
Furthermore, is the if (isFirst != 'True') clause the right way to init the ko view model?

You are dealing with an asynchronous operation (an Ajax request). These operations do not have return values. Therefore, this can never work:
ko.mapping.fromJS(search('someSearch', 'True'));
That's what the success callback is for. Incoming data can only be handled there.
function search(queryString, targetObservable) {
$.ajax({
type: 'POST',
url: 'url',
data: { 'searchQuery': queryString },
dataType: 'json',
success: function (dt) {
ko.mapping.fromJS(dt, targetObservable);
}
});
};
search('someSearch', PostsViewModel);

Related

Ajax sends two empty arrays and one filled, PHP receives only the full one

I'm completely new to PHP. Working with ajax, php and WordPress I'm sending an object with ajax:
let import_data = {
action: 'import_data',
data: {
first_array: [], // this one is empty
second_array: [], // this one too
third_array: [1,2,3] // this one is full
}
};
I've checked the import_data object many times right before it was sent. The php, however, always receives:
import_data = {
action: 'import_data',
data: {
third_array: [1,2,3]
}
}
The question is why is that happening and how can I achieve receiving all arrays, whether they are empty or not, i.e.
import_data = {
action: 'import_data',
data: {
first_array: [],
second_array: [],
third_array: [1,2,3]
}
}
I need to refactor a lot of code now due to this issue so I'm trying to solve it as easy as possible, but if there is a common known right way to deal with it I'll use it. Thanks in advance!
P.S. In case you wondering, yes, if all arrays being sent are full, php will receive all of them.
UPD In the comments I got I might've wanted to add contentType or json.strngify my data. It didn't help, but I might do it wrong, so I'll try to partly show my code below:
var import_data = {
action: 'start_import',
sliced_batch: {
rows_to_add: [],
rows_to_delete: [],
rows_to_update: [1,2,3,4,5,...]
}
};
function ajax_call(import_data) {
// ... processes
jQuery.ajax({
url: start_import_ajax.url, // url from php file
type: 'POST',
contentType: "text; charset=utf-8", // sending string with cyrillic (ukrainian lng)
dataType: 'application/json', // want to recieve json
data: JSON.stringify(import_data),
success: function(response) {
// ... processes import_data ...
if(it is the end of data) return;
else ajax_call(import_data);
},
error: function(e) {
// here is where I end up
}
}
PHP side is now pretty shy, as I just made a pause and wanted to see my data in console:
function start_import_callback() {
echo json_decode($_POST);
echo $_POST;
echo json_decode($_POST['sliced_batch']);
echo $_POST['sliced_batch'];
wp_die();
}
I've tried all echo's one by one, but always saw:
{
"readyState": 4,
"responseText": "0",
"status": 400,
"statusText": "error"
}
When NOT stringifying and NOT specifying contentType/dataType it returns:
{
action: 'import_data',
sliced_batch: {
rows_to_update:
{
"ID": "00000006125",
"CatalogueNumber": "bla, bla",
"Category": "bla, bla",
"Manufacturer": "bla",
"Nomenclature": "blablablablabla",
"NomenclatureUkrainian": "bla",
"StockStatus": "instock",
"Price": "2 315",
"Parent": "blabla",
"Sorting": "99"
},
{},...
]
}
}
So, rows_to_delete: [] and rows_to_add: [] are missing...
You are using jQuery dataType options wrong!
The dataType: value should be 'json' not 'application/json' because your value will request with HTTP accept: */* but if you use 'json' it will be accept: application/json.
Option 1
Use content type application/json.
The contentType: should be 'application/json' or 'application/json;charset=utf-8'.
By this content type you will be now able to receive POST data in JSON but you cannot access them with $_POST because the data is not valid application/x-www-form-urlencoded.
Full code for client side:
var import_data = {
action: 'start_import',
sliced_batch: {
rows_to_add: [],
rows_to_delete: [],
rows_to_update: [1,2,3,4,5]
}
};
function ajax_call(import_data) {
// ... processes
jQuery.ajax({
url: 'test.php', // url from php file
type: 'POST',
contentType: "application/json;charset=utf-8", // sending string with cyrillic (ukrainian lng)
dataType: 'json', // want to recieve json
data: JSON.stringify(import_data),
success: function(response) {
// ... processes import_data ...
},
error: function(e) {
// here is where I end up
}
});
}
Code for PHP:
$data = json_decode(file_get_contents('php://input'), true);
// use $data['sliced_batch'] to access `rows_to_add`, `rows_to_delete` etc.
Option 2
Use content type application/x-www-form-urlencoded.
With this content type, you will be able to access $_POST properly.
However, to use this request content type in header, the jQuery itself will be modify the value if it is empty jQuery will be just delete it!! So, you need to JSON string only sliced_batch property.
Here is the JS code:
var import_data = {
action: 'start_import',
sliced_batch: {
rows_to_add: [],
rows_to_delete: [],
rows_to_update: [1,2,3,4,5]
}
};
function ajax_call(import_data) {
// ... processes
// modify `sliced_batch` property to be JSON string to prevent jQuery remove empty properties.
import_data.sliced_batch = JSON.stringify(import_data.sliced_batch);
jQuery.ajax({
url: 'test.php', // url from php file
type: 'POST',
// just remove contentType option. It is no need.
//contentType: "application/json;charset=utf-8", // sending string with cyrillic (ukrainian lng)
dataType: 'json', // want to recieve json
data: import_data,
success: function(response) {
// ... processes import_data ...
},
error: function(e) {
// here is where I end up
}
});
}
PHP:
$sliced_batch = ($_POST['sliced_batch'] ?? '');
$sliced_batch = json_decode($sliced_batch, true);
// you can now access $sliced_batch['rows_to_add'], etc...
So, thanks once again to #vee for his explanation, but here's one more thing I'd like to share as it was crucial to get everything to work.
First, for json_decode method the JS object keys should be double-quoted, i.e. NOT
$bad_json = '{ bar: "baz" }';
json_decode($bad_json); // null
or
$bad_json = '{ 'bar': "baz" }';
json_decode($bad_json); // null
BUT
$bad_json = '{ "bar": "baz" }';
json_decode($bad_json); // array("bar" => "baz")
Second and most important!
When dealing with WordPress it sets its own rules and shows focuses!
Depending on what answer you'd like to get, you may want to use function wp_unslash(). Looking at the stringified data in console I saw somth like this:
"\u0421\u0435\u0440\u0432\u0435\u0440: \u0424\u0430\u0439\u043b\u0456\u0432 av_imp_p_WEB.csv \u0456 av_imp_p_WEB_previous.csv \u043d\u0435 \u0431\u0443\u043b\u043e \u0432\u0438\u044f\u0432\u043b\u0435\u043d\u043e. \u041f\u043e\u0447\u0438\u043d\u0430\u044e \u0456\u043c\u043f\u043e\u0440\u0442 \u0432\u0441\u044c\u043e\u0433\u043e
// it is more common for contentType: "application/x-www-form-urlencoded"
It is the dirty work of WooCommerce (as I read from another's people opinion) and it hinders parsing it the right way, so my full code is:
JS
var import_data = {
"action": "start_import",
"sliced_batch": {
"rows_to_add": my_data1,
"rows_to_delete": my_data2,
"rows_to_update": my_data3
}
};
function ajax_call(import_data) {
// ... processes
jQuery.ajax({ // ajax to php to save the file to uploads and move it to the plugin's folder
url: start_import_ajax.url, // url from php file
type: 'POST',
//contentType: "application/json;charset=utf-8", // what you send
dataType: 'JSON', // what you would like as a response
data: {
"action": import_data.action,
"sliced_batch": JSON.stringify(import_data.sliced_batch)
},
success: function(response) {
//response = JSON.parse(response); // if you'd like to console.log what you've sent
console.log(response);
}
....
PHP
$sliced_batch = wp_unslash($_POST['sliced_batch']);
$sliced_batch = json_decode($sliced_batch, true);
$result = start_import($sliced_batch);
if($result == 0) {
echo json_encode(["status" => 0]);
} else echo json_encode(["status" => 1]);

Use form naming to reach json data in Javascript/jQuery?

I have the following input name: dynamic[elements][1][slider][image1]
When performing an ajax call, a json response with settings and its value is returned.
$.ajax({
url: '/get/settings',
type: 'POST',
data: formData,
async: false,
success: function (data) {
});
How can i get the value of dynamic[elements][1][slider][image1] the easiest way? It works to get the value like this:
data.dynamic.elements[1].slider.image1
So:
$.ajax({
url: '/get/settings',
type: 'POST',
data: formData,
async: false,
success: function (data) {
console.log(data.dynamic.elements[1].slider.image1);
});
But isn't their any better way of getting the value? The only identifier I have to get the value, is the name of the input field which is dynamic[elements][1][slider][image1]. So i would need to extract this string and put it together as data.dynamic.elements[1].slider.image1 to then make it a dynamic variable somehow (to finally get the value)?
Example ajax response:
{
"success": 1,
"dynamic": {
"elements": [
{
"title": {
"title": "Our beautiful topic"
}
},
{
"slider": {
"image1": "5zw3ucypzp3qham.png",
"image1_link": "hellor"
}
}
]
}
}
You may choose to write a generic function for the purpose of retrieving data from object. The function should look something like below. Though the function may not be foolproof but should be enough for proof-of-concept.
function getObjectData(target, path) {
// if the path is not in dot notation first convert
if (path.indexOf(".") == -1)
path = path.replace(/\[/g, ".").replace(/\]/g, "");
var parts = path.split(".");
return parts.reduce(function (acc, currentVal) {
return acc ? (acc[currentVal] || undefined) : acc;
}, target);
}
//usage
getObjectData(data, "dynamic.elements.0.slider.image1"); //undefined
getObjectData(data, "dynamic.elements.1.slider.image1"); //5zw3ucypzp3qham.png
getObjectData(data, "dynamic[elements][1][slider][image1]"); //5zw3ucypzp3qham.png
Hope this helps.

passing an array of int to MVC controller using ajax javascript

What am I doing wrong here?
I can successfully pass 4 bool params to a controller. Now I want to pass an array of int to my controller but it doesn't work - I've left my working code in the example(commented out) so you can see I'm not changing that much - I think I'm missing something simple (it is 17:44 afterall!!!). I can see the array is populated using the alert(rolesChecked); statement:
var rolesChecked = [];
$('[type="checkbox"].role-checkbox').each(function () {
if (this.checked)
{
rolesChecked.push($(this).val());
}
});
alert(rolesChecked);
//var administrator = $('#cbAdministrator').is(":checked");
//var manager = $('#cbManager').is(":checked");
//var technician = $('#cbTechnician').is(":checked");
//var transcriber = $('#cbTranscriber').is(":checked");
if (rolesChecked.count > 0){//administrator || manager || technician || transcriber) {
$.ajax({
url: '#Url.Action("GetFutureHolidays", "Employee")',
type: 'GET',
dataType: 'json',
// we set cache: false because GET requests are often cached by browsers
// IE is particularly aggressive in that respect
cache: false,
data: {
roleIdXXXs: rolesChecked
//includeAdministrator: administrator,
//includeManager: manager,
//includeTechnician: technician,
//includeTranscriber: transcriber
},
success: function (data) {
//do something...
}
});
}
Controller Action:
public string GetFutureHolidays(List<int> roleIdXXXs)//bool includeAdministrator, bool includeManager, bool includeTechnician, bool includeTranscriber)
{
//do something
}
with the old code, the controller action would be hit... with the array, it never gets hit... What am I missing here...
also, I think List<int> roleIdXXXs should be fine, but I also tried List<string>, int[] and string[] in case it isn't!!!
You need to add the traditional: true ajax option to post back an array to the collection
$.ajax({
url: '#Url.Action("GetFutureHolidays", "Employee")',
type: 'GET',
dataType: 'json',
cache: false,
data: { roleIdXXXs: rolesChecked },
traditional: true, // add this
success: function (data) {
}
});
Refer also the answer to this question for more detail on what the options does and the form data it generates.
In your if statement, instead of rolesChecked.count, use rolesChecked.length
You can't submit a list like this from Ajax, the quickest fix in the process you are using is to use serialization-desalinization process, you can send it as
roleIdXXXs: JSON.stringify(rolesChecked)
on Action:
public ActionResult GetFutureHolidays(string rolesChecked)
{
var test = new JavaScriptSerializer().Deserialize<List<int>>(rolesChecked);
}
You should use JSON.stringify() on you AJAX call lice this:
data: {
roleIdXXXs: JSON.stringify(rolesChecked)
//includeAdministrator: administrator,
//includeManager: manager,
//includeTechnician: technician,
//includeTranscriber: transcriber
}

Can not seem to pass more than one variable with jquery to mysql

I have seen several examples and can't seem to get the hang of passing more than one variable to mysql using jquery. Here is my situation:
I have a page with 2 cascading drop downs,( they work great using jquery to update second drop down based on the first drop down.)
when the first drop down is selected jquery updates the second drop down AND passes the customer id to a php script that creates a new record in the tblinvoice table (this also works great no problems.)
when the second drop down is selected I need to pass that value along with the invoice number to my php script so I can update the record with the instid.(this is the part that don't work)
If I only pass the instid and manually put the invoice number in the where clause of the query all works fine. If I omit the where clause all records are updated as expected. I need to know what I am doing wrong or what is missing.
I will try to post the code here
jquery code
$(document).ready(function() {
$("select#cust").change(function() {
var cust_id = $("select#cust option:selected").attr(
'value');
var test = $("#test").val();
var din = $("#idate").val();
$("#inst").html("");
if (cust_id.length > 0) {
$.ajax({
type: "POST",
url: "fetch_inst.php",
data: "cust_id=" + cust_id,
cache: false,
beforeSend: function() {
$('#inst').html(
'<img src="loader.gif" alt="" width="24" height="24">'
);
},
success: function(html) {
$("#inst").html(html);
}
});
if (test == 0) {
$.ajax({
type: "POST",
url: "wo_start.php",
data: "cust_id=" + cust_id,
cache: false,
beforeSend: function() {
},
success: function(html) {
$("#invoice").html(html);
$("#test").val(1);
var inum = $("#inv").val();
$("#invnum").val(din +
"-" + inum);
}
});
}
}
});
$("select#inst").change(function() {
var inst_id = $("select#inst option:selected").attr(
'value');
var custid = $("select#cust option:selected").attr(
'value');
var invid = # ("#inv").val()
if (inst_id.length > 0) {
$.ajax({
type: "POST",
url: "wo_start.php",
data: {
inst_id: inst_id,
}
cache: false,
beforeSend: function() {
},
success: function() {
}
});
}
});
});
I have tried using data: {inst_id:inst_id,custid:custid,invid:invid,} (no update to the table like this)
I also tried data: "inst_id="+inst_id+"&custid="+custid+"&invid="+invid,(this also gives no results.)
Can someone PLEASE look at this jquery and see if I am making a simple error?
Try this format:
data: { inst_id: inst_id, custid: custid, invid: invid },
You can post a JSON object to the server so long as you serialize it and then let the server know the data type.
First you need to define your JSON object:
var postData = { inst_id: inst_id, custid: custid, invid: invid };
Then update your ajax to use the serialized version of that object and let the server know the data type:
$.ajax({
type: "POST",
url: "fetch_inst.php",
data: JSON.stringify(postData),
contentType: "application/json",
..continue the rest of your ajax....

JSON pass null value to MVC 4 controller in IE9

I got some problem while posting JSON data into MVC 4 controller.
Below method is working fine in Firefox but unfortunately failed in IE 9
The JavaScript :
var newCustomer = {
CustName: $("#CustName").val(),
CustLocalName: $("#CustLocalName").val(),
CustNumber: $("#CustNumber").val(),
CountryID: $("#SelectCountry").val(),
City: $("#City").val()
};
$.ajax({
url: '#Url.Content("~/CustomerHeader/CreateCustomerHeader")',
cache: false,
type: "POST",
dataType: "json",
contentType: 'application/json; charset=utf-8',
data: JSON.stringify(newCustomer),
success: function (mydata) {
$("#message").html("Success");
},
error: function () {
$("#message").html("Save failed");
}
});
and this is my controller :
public JsonResult CreateCustomerHeader(CustomerHeader record)
{
try
{
if (!ModelState.IsValid)
{
return Json(new { Result = "ERROR", Message = "Form is not valid! Please correct it and try again." });
}
RepositoryHeader.Update(record);
return Json(new { Result = "OK", Record = record});
}
catch (Exception ex)
{
return Json(new { Result = "ERROR", Message = ex.Message });
}
}
the "data" variable as in public JsonResult CreateCustomerHeader(CustomerHeader **data**) is getting NULL but while using FireFox it holds the correct value.
UPDATE : New method trying using $.post
function CreateNewCustomer(newCustomer) {
$.post("/CustomerHeader/CreateCustomerHeader",
newCustomer,
function (response, status, jqxhr) {
console.log(response.toString())
});
}
Based off the bit that you've shown, this is a simplified variation that may work more consistently, using jQuery.post() (http://api.jquery.com/jQuery.post/):
var data = {
CustName: $("#CustName").val(),
CustLocalName: $("#CustLocalName").val(),
CustNumber: $("#CustNumber").val(),
CountryID: $("#SelectCountry").val(),
City: $("#City").val()
};
$.post({
'#Url.Action("CreateCustomerHeader", "CustomerHeader")',
data,
function(response, status, jqxhr){
// do something with the response data
}).success(function () {
$("#message").html("Success");
}).error(function () {
$("#message").html("Save failed");
});
$.post() uses $.ajax as it's base, but abstracts some of the details away. For instance, $.post calls are not cached, so you don't need to set the cache state (and setting it is ignored if you do). Using a simple JavaScript object lets jQuery decide how to serialize the POST variables; when using this format, I rarely have issues with the model binder not being able to properly bind to my .NET classes.
response is whatever you send back from the controller; in your case, a JSON object. status is a simple text value like success or error, and jqxhr is a jQuery XMLHttpRequest object, which you could use to get some more information about the request, but I rarely find a need for it.
first of all I would like to apologize #Tieson.T for not providing details on JavaScript section of the view. The problem is actually caused by $('#addCustomerHeaderModal').modal('hide') that occurred just after ajax call.
The full script :
try{ ..
var newCustomer =
{
CustName: $("#CustName").val(),
CustLocalName: $("#CustLocalName").val(),
CustNumber: $("#CustNumber").val(),
CountryID: $("#SelectCountry").val(),
City: $("#City").val()
};
$.ajax({
url: '/CustomerHeader/CreateCustomerHeader',
cache: false,
type: "POST",
dataType: "json",
data: JSON.stringify(newCustomer),
contentType: "application/json; charset=utf-8",
success: function (mydata) {
$("#message").html("Success");
},
error: function () {
$("#message").html("Save failed");
}
});
}
catch(Error) {
console.log(Error.toString());
}
//$('#addCustomerHeaderModal').modal('hide')//THIS is the part that causing controller cannot retrieve the data but happened only with IE!
I have commented $('#addCustomerHeaderModal').modal('hide') and now the value received by controller is no more NULL with IE. Don't know why modal-hide event behave like this with IE9.
Thanks for all the efforts in solving my problem guys :-)

Categories