DataType property Raw suddenly Not working for the New PHP8.1 update. Is there a better way to post this data?
We are passing Website data to FormStack for a PDF print. This has been working for 5 years until last week an new PHP8.1 update has suddenly broke our system.
"Due to our new PHP 8 update, any time you have a field that looks like {$TestField.value}
where the object has a key attached to it, it's going to need a value for that
key.TestField = Object
value = key
value will always need a value assigned to it, even if it is an empty value, or PHP 8 will cause an error.
We've noticed many customers sending over an array, or nothing at all, and the key that field is expecting is not in the data. This would be a silent error in the previous PHP version, and it would have removed that field completely. In the new version, rather than removing the field, it shows an error and prevents the Document from merging.There are two fixes for this currently -
You'll need to add an{if is_array($TestField)}{$TestField.Name}{/if}for every field. This will check if the key is present in the object coming over. If it isn't, it will remove that field completely. This is more of a bandaid fix and will require you to do it for every field experiencing this issue.
Making sure that the object you're sending over has a key present for every key used in merge fields, even if that value is blank. As long as Documents can map that key to something, the Document should merge just fine. The issue arises when there isn't a value for the key to map to.
https://www.webmerge.me/manage/documents?page=overview&document_
type here
$(document).on('knack-scene-render.scene_316', function(event, scene) {
// link hander: send to webmerge
$('#view_684') .submit(function( event) {
event.preventDefault();
// get data
var data = Knack.models['view_684'].toJSON();
var data_703 = Knack.models['view_703'].toJSON();
var data_704 = Knack.models['view_704'].toJSON();
var data_705 = Knack.models['view_705'].toJSON();
var data_875 = Knack.models['view_875'].toJSON();
var data_1161 = Knack.models['view_1161'].toJSON();
var models = Knack.models['view_807'].data.models; //tableview
var items =[]; //table items
for(var i = 0; i < models.length; i++){
items.push({
product: models[i].attributes.field_1594_raw, //qty
price: models[i].attributes.field_1593_raw, //packaging
total: models[i].attributes.field_1595_raw, //desc
}); //
}//
log('data!');log(data);log(items);
Knack.showSpinner();
$.ajax({
url: 'https://www.webmerge.me/route/XXXX',
data: {
items: items,
AZPONUM: data_703.field_1677_raw,
OldPO: data_703.field_1477_raw,
PickupDate: data_703.field_1407_raw,
OriginAddress: data_703.field_1375_raw,
LocationAddress: data_703.field_1044_raw,
Packaging: data_703.field_1314_raw,
CustomerPOnum: data_703.field_1409_raw,
CarrierName: data_703.field_1635_raw,
ProductType: data_703.field_1046_raw,
DeliveryDate: data_703.field_1408_raw,
CustomerName: data_703.field_1424_raw,
SKUNum: data_703.field_1426_raw,
UnitCount: data_703.field_1315_raw,
OrderContact: data_703.field_1483_ra,
TrailerType: data_703.field_1967_raw,
EmployeeEmail: data_703.field_1484_raw,
CarrierContactName: data_703.field_1485_raw,
CarrierPhone: data_703.field_1486_raw,
MainContact: data_703.field_1489_raw,
MainPhone: data_703.field_1490_raw,
MainCell: data_703.field_1491_raw,
AltContact: data_703.field_1492_raw,
AltPhone: data_703.field_1493_raw,
OrderCommentsDE: data_703.field_1494_raw,
BulkorBags: data_703.field_1503_raw,
producttypename: data_703.field_1510_raw,
ProductDesc: data_703.field_1508_raw,
SalesmanEmail: data_703.field_1500_raw,
ShipmentType: data_703.field_1342_raw,
ExpireDate: data_703.field_1509_raw,
OrderDate: data_703.field_1428_raw,
QTY: data_703.field_1047_raw,
ShippingMarks: data_703.field_1675_raw,
CurrentUserEmail: data_703.field_1668_raw,
CustomerEmail: data_703.field_1684_raw,
CarrierContactEmail: data_703.field_1683_raw,
LocationContactEmail: data_703.field_1685_raw,
CancelStatus: data_703.field_1507_raw,
Transloaded: data_703.field_1686_raw,
Revision: data_703.field_1688_raw,
PrintCounter: data_703.field_1717_raw,
COFA: data_703.field_1761_raw,
MSDS: data_703.field_1887_raw,
CustomerType: data_703.field_1431_raw,
FreightRateShow: data_703.field_1974_raw,
FSTrigger: data_703.field_1983_raw,
OceanCarrier: data_703.field_2077_raw,
FFnum: data_703.field_2079_raw,
OceanBookNum: data_703.field_2078_raw,
DistroContact: data_703.field_2244_raw,
Distributor: data_703.field_2243_raw,
},
type: 'POST',
success: function() {
alert('Successfully Sent!');
Knack.hideSpinner();
},
error: function() {
alert('There was an error creating the Delivery Coordination Form');
}
});
});
});
Related
I have some JS that stores the name and value of selected checkboxes on one page and then, on a button click, adds this data to a table on page 2.
This works, but now I am looking to do the same for a textbox containing a number. Specifically, I'm looking to take the value entered by the user and add this to a cell in the table. What would be the best way to approach this? Add to the existing function or create a separate on button click function specifically for the textbox value?
I have added a screenshot of the HTML table on page 2 along with where I would like the textbox value to go (highlighted with a red rectangle).
Here's what I have so far:
HTML for textbox (page 1):
<div class="selecttier">
<h1>5. Number of Clicks</h1>
<input id="numberofclickstextbox" name="numberofclicks" type="text" value="0" data-total="0" oninput="calculatetier()" />
</div>
JS on page 1:
$('#sales_order_form_button').click(function() {
let table_info = [];
$('input[type=checkbox]').each(
function(index, value) {
if($(this).is(':checked')) {
table_info.push(
{
name: $(this).attr('name'),
value: $(this).attr('value'),
}
);
}
});
let base64str=btoa(JSON.stringify(table_info));
window.location = "page2.html?table_data=" + base64str;
});
JS on page 2:
// Helper function
function getUrlParameter(name) {
name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
var regex = new RegExp('[\\?&]' + name + '=([^&#]*)');
var results = regex.exec(location.href);
return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
};
// actual code
let table_data = getUrlParameter('table_data');
let data_from_page_1 = JSON.parse(atob(table_data));
for(let i = 0; i < data_from_page_1.length; i++){
let row = $("<tr></tr>");
let recordName = $("<td></td>").text(data_from_page_1[i].name);
let recordValue = $("<td></td>").text(data_from_page_1[i].value);
row.append(recordName, recordValue);
$('#output_table').append(row);
}
// code to sum CPC column
var sum1 = 0;
$("#output_table tr > td:nth-child(2)").each(
(_,el) => sum1 += Number($(el).text()) || 0
);
$("#sum1").text(sum1);
//datetime stamp
var dt = new Date();
document.getElementById("datetime").innerHTML = dt.toLocaleString();
Output HTML table (page 2):
<table id="output_table">
<tr>
<th>Name</th>
<th>Value</th>
<th>Number of Clicks</th>
</tr>
<tfoot>
<tr>
<th id="total" colspan="1">Total CPC:</th>
<td id="sum1"></td>
</tr>
</tfoot>
</table>
As stated in the #Manu Varghese comment, the way to go would be using sessionStorage or localStorage.
First, let's differentiate both. According to the Stack Overflow question "HTML5 Local storage vs Session Storage", we have the following answer:
localStorage and sessionStorage both extend Storage. There is no difference between them except for the intended "non-persistence" of sessionStorage.
That is, the data stored in localStorage persists until explicitly deleted. Changes made are saved and available for all current and future visits to the site.
For sessionStorage, changes are only available per tab. Changes made are saved and available for the current page in that tab until it is closed. Once it is closed, the stored data is deleted.
Considering they are used the same way and you must to choose between what better fits your case, I will proceed using sessionStorage.
For that, in the first page you must use:
sessionStorage.setItem("key", "value")
You may set the item right when you perceives a change, like in the input 'blur' event.
and when you land in the second page (right when jQuery calls its start event), you will retrieve your data using:
sessionStorage.getItem("key")
Take in mind that localStorage/sessionStorage can support a limited amount of data. Even if that limit is way bigger than URL, most browsers will store only 2.5MB to 10MB per origin, according to the browser implementation (you may test by yourself in the link recommended in MDN (Mozilla Development Network), http://dev-test.nemikor.com/web-storage/support-test/).
Also, you may want to avoid storing sensitive data in the storages, due to some some discussions about security, which seems not to be a complaint here.
Implementation in the given case
Your code can be modified in three steps:
Change the way you save the data to use the storage
Creates a JSON of an object containing the array, instead the make the JSON using the array itself. Then you can add more fields.
Load the JSON object and its fields (the array and the number).
Step 1 - Changing to sessionStorage
Just now you have your Javascript on page 1 creating an array of data and stringifying that data to a JSON string.
If you want to use the storage rather than the URL for all the data, just change these lines of code from:
let base64str=btoa(JSON.stringify(table_info));
window.location = "page2.html?table_data=" + base64str;
to the code that will save the data into a (local/session)Storage:
let jsonStr=JSON.stringify(table_info); // converts to JSON string
sessionStorage.setItem("oldData", jsonStr); // save to storage
window.location = "page2.html"; // navigate to other page
Notice that the storage can receive any string, but only strings, then we can remove the btoa function, but we must keep the stringify.
Step 2 -- Adding more data to save
Now you have one JSON that is an array of items. But what do you want is to include one more field, parallel to this array. Of course, you can't include it in the array, as it is a different thing. So, what we must to do is to create a JSON object which has a number field AND the array field itself.
Your function to create the array is all ok, then we will use the same "table_data" as the array and include it to a new JSON object:
let table_data = []; // the array you have
$('input[type=checkbox]').each(
... rest of code ...
); // the function that creates the array (I abbreviated it here)
// Creates an object with an array and a number
let jsonObj = {
table_data: table_data,
number_of_clicks: theNumberYouHave/* your variable with the number here */
};
// This is the bit above with CHANGES into variable names
// Instead of "table_data", now we save "jsonObj"
let jsonStr=JSON.stringify(jsonObj); // converts the "jsonObj" to a JSON string
sessionStorage.setItem("oldData", jsonStr);
window.location = "page2.html";
Remember to change "theNumberYouHave" to whatever your variable with the number is called. The you will append the number as a field of the JSON object.
In other words, this simply will create an structure like that:
{
number_of_clicks: 5216,
table_data: [
{ name: "...", value: "..."},
{ name: "...", value: "..."},
{ name: "...", value: "..."},
...
]
}
See? Your table_data is still there, but with a new sibling (number_of_clicks) inside an object.
Step 3 -- Loading data from page 1
For now, you have these two lines of code in page 2 to read data from page 1:
let table_data = getUrlParameter('table_data');
let data_from_page_1 = JSON.parse(atob(table_data));
What do you need there, is to simply replace the getUrlParameter function to read from the storage, and remove the atob function to reflect the changes we made in page 1, this way:
let jsonObj = sessionStorage.getItem("oldData"); // reads the string
let data_from_page_1 = JSON.parse(jsonObj); // parse the JSON string
let table_data = data_from_page_1.table_data; // grab the table data
let number_of_clicks = data_from_page_1.number_of_clicks; // grab the number
Now you are free to use the variable "table_data" like you did, and to use the "number_of_clicks" in the way you want to use it. It is the number passed from page 1, then you may set it to your table cell.
You have it with the unique ID "sum1", the you may just:
$("#sum1").text(number_of_clicks);
And you are done!
I highly recommend localStorage and sessionStorage to be used, as per this and this
Page 1 code full source
$('#next_page_button').click(function(){
let table_info = [];
// Do for checkboxes
$('.campaignstrategy input[type=checkbox]').each(
function(index, value){
if($(this).is(':checked')){
table_info.push(
{
name: $(this).attr('name'),
value: $(this).attr('value'),
type: 'checkbox'
}
);
}
});
$('.campaignstrategy input[type=text]').each(
function(index, value){
table_info.push(
{
name: $(this).attr('name'),
value: $(this).attr('value'),
type: 'text'
}
);
});
let base64str=btoa(JSON.stringify(table_info));
window.location = "page2.html?table_data=" + base64str;
});
Page 2 Code full source
// Helper function
function getUrlParameter(name) {
name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
var regex = new RegExp('[\\?&]' + name + '=([^&#]*)');
var results = regex.exec(location.href);
return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
};
// actual code
let table_data = getUrlParameter('table_data');
let data_from_page_1 = JSON.parse(atob(table_data));
// clear table
$('#output_table').html("");
// generator checboxes
for(let i=0;i<data_from_page_1.length;i++){
if(data_from_page_1[i].type == "checkbox"){
let row = $("<tr></tr>");
let recordName = $("<td></td>").text(data_from_page_1[i].name);
let recordValue = $("<td></td>").text(data_from_page_1[i].value);
let recordCount = $("<td></td>").text("");
row.append(recordName, recordValue, recordCount); // not used but needed
$('#output_table').append(row);
}
}
// generate textboxes
for(let i=0;i<data_from_page_1.length;i++){
if(data_from_page_1[i].type == "text"){
let row = $("<tr></tr>");
let recordName = $("<td></td>").text("");
let recordValue = $("<td></td>").text("");
let recordCount = $("<td></td>").text(data_from_page_1[i].value);
row.append(recordName, recordValue, recordCount);
$('#output_table').append(row);
}
}
ANSWER:
What would be the best way to approach this?
window.localStorage - stores data with no expiration date
window.sessionStorage - stores data for one session
I have a form with a textbox title, dropdown menu year and a button. When I click the button, I want to get the values of title and year as properties to an object Movie. Then save them to a table on parse.com. The code below adds a recording to the table with values undefined.
<script>
function saveValues() { // function is appended as onclick to button
var $titleValue = $('#inputTitle').val();
var $select = $('#select');
var $yearValue = $select.val();
var Movie = Parse.Object.extend("Movie");
var movie = new Movie();
// movie.set('title', $titleValue); // Doesn't work. Returns undefined
// movie.set('year', $yearValue); // Doesn't work. Returns undefined
movie.title = $titleValue; // Works
movie.year = $yearValue; // Works
alert(movie.title); // Returns the value
alert(movie.year); // Returns the value
alert(movie); // Returns [object Object]. I was expecting {title: '<SOMETITLE>', year: '<SOMEYEAR>'}
console.log(movie); // This prints a lot of stuff and title and year are there with the respective values.
movie.save()
.then(function(object) {
alert("yay! it worked");
})
}
</script>
Note that when I try to save only the title to the table, it works fine.
Without seeing your full code, I can't guarantee that this will work, but give this a try:
movie.save({
title : $titleValue,
year : $yearValue,
}, {
success: function(movie) {
alert("movie saved successfully with title: " + movie.get("title") +
", and year: " + movie.get("year"));
},
error: function(error) {
alert("error, movie save failed. error code: " + error.code + ", " error.message);
}
});
At the very least you will have a descriptive error message that will tell you what went wrong. Based on the fact that you said it works when you only save the title and not the year, I suspect it may be because your 'year' field in Parse is stored as a number, but you are passing it in as a string (since it came from an HTML form, I'm assuming).
If that doesn't work, I also suspect it may have something to do with appending this function to your button onload rather than as a click or a submit. But that wouldn't explain why it still works if you just leave out the year.
Finally, I wonder if Parse's SDK is confused by the '$' symbol at the beginning of your variable names, but I don't see why that would be the case.
OK, after some time I found out what the problem was. And it was a silly one. It turns out that the value in option was of type 'string'. By adding parseInt() like so: var $yearValue = parseInt($select.val());. And then movie.save({title: titleValue, year: yearValue})..... This way everything works. Initially, I had tried putting key-value pairs in the save() but the year value wasn't the right type.
So note to anyone out there - check your data types!
I'm having a problem getting an array of information stored properly as JSON.
I made a fiddle to illustrate the problem. Enter a set of tags and take a look at the console to see the output.
More explanation:
So I have an input that takes in a comma-separated list of tags, which I then format.
function createTagArray() {
// given an input value of 'tag1, tag2, tag3'
// returns array = ['tag1', 'tag2', 'tag3']
}
I thought what I needed to do next was the following:
loop over the array and create a 'tag' object for each item which also includes an id for the tag and the id of the contact the tag is associated with.
Each object is pushed to tags, an observable array.
function single_tag(id, contactId, tagLabel) {
var self = this;
self.id = id;
self.contactId = contactId;
self.tagLabel = tagLabel;
}
function createTags() {
var array = createTagArray();
for (var i = 0; i < array.length; i++) {
self.tags().push(new single_tag(uuid.generate(), self.contactId, array[i]));
}
}
Then, I converted it into JSON
self.contactInformation = function() {
return ko.toJS({
"id": self.contactId,
"firstname": self.firstname(),
"lastname": self.lastname(),
... other fields ...
"tags": self.tags(),
})
}
But, when I inspect the console output of calling this function, tags is a collection of arrays, not a nice json object.
How do I get it formatted correctly?
I tried this suggestion, and the tag json is structured correctly, but it is stored with escaped quotes, so that seems wrong.
Thanks for all the help!
I would recommend you knockout.mapping plugin for KO, it allow map complicated JSON structure to view model, even without declarations.
From the documentation
Let’s say you have a JavaScript object that looks like this:
var data = {
name: 'Scot',
children: [
{ id : 1, name : 'Alicw' }
]
}
You can map this to a view model without any problems:
var viewModel = ko.mapping.fromJS(data);
Now, let’s say the data is updated to be without any typos:
var data = {
name: 'Scott',
children: [
{ id : 1, name : 'Alice' }
]
}
Two things have happened here: name was changed from Scot to Scott and children[0].name was changed from Alicw to the typo-free Alice. You can update viewModel based on this new data:
ko.mapping.fromJS(data, viewModel);
And name would have changed as expected. However, in the children array, the child (Alicw) would have been completely removed and a new one (Alice) added. This is not completely what you would have expected. Instead, you would have expected that only the name property of the child was updated from Alicw to Alice, not that the entire child was replaced!
...
To solve this, you can specify which key the mapping plugin should use to determine if an object is new or old. You would set it up like this:
var mapping = {
'children': {
key: function(data) {
return ko.utils.unwrapObservable(data.id);
}
}
}
var viewModel = ko.mapping.fromJS(data, mapping);
In the jsfiddle you were using Knockout 3.0 which doesn't have support for textInput. This was added in 3.2. To use version 3.2 you need to use a cdn such as this: http://cdnjs.com/libraries/knockout
There was typeo in your binding. sumbit should be submit.
There was a problem with your constructor for single_tag. id was not used so I removed it:
function single_tag(contactId, tagLabel) {
var self = this;
self.contactId = contactId;
self.tagLabel = tagLabel;
}
Currently also contactId is not set because the observable has not been set to a value.
To convert to JSON you need to use ko.toJSON instead of ko.toJS:
self.contactInformation = function() {
return ko.toJSON({
"firstname": self.firstname(),
"tags": self.tags(),
})
}
Now when the console writes out an array appears:
{
"firstname":"test",
"tags":[
{"tagLabel":"test1"},
{"tagLabel":"test2"},
{"tagLabel":"test3"}
]
}
JsFiddle
So my problem was more basic than I was realizing. I'm using JSON Server to serve up my data, and I was pulling information from two parts of the database (contacts & tags).
When I tried to update my tags, I was trying to apply them to a property that didn't exist on the contact JSON in my database. Posting the tags separately worked though.
I have used an ajax call to retrieve data from Googles places api and I have used a for/in loop to begin to pull the information. the information that I am looking for logs to the console, but I cannot get it to display in an array how I would like it to.
Right now I am getting a list of names when I request names which is a parameter in the JSON object. Is there a way to get it into an array so the I can randomly select one of the values from the array?
at this point the way it is returning my data, when I run a script to pull a random string, it pulls a random letter out of the last value to be found when searching through my JSON object.
Here is my code. Not sure if I explained myself clearly but it's the best that I could word what I am looking to do. Thanks.
// **GLOBAL VARIABLES** //
// Chosen Restaurant display area
var display = document.getElementById('chosenVenue');
// Grab button
var button = document.getElementById('selectVenue');
// Sample Array that gets queried
var venueData = ["McDonalds", "Burger King", "Wendys"];
// Grab JSON data from Google
$(document).ready(function(){
$.ajax({
url: 'hungr.php', // Send query through proxy (JSONP is disabled for Google maps)
data: { requrl: "https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=40.7484,-73.9857&radius=800&sensor=false&keyword=restaurants,food&key=AIzaSyBkCkXIHFjvqcqrRytSqD7T_RyFMNkR6bA&callback=?"}, // URL to query WITH parameters
dataType: "json",
type: "GET", // or POST if you want => update php in that case
success: function (data) {
// Traverse the array using for/in loop using i as var
for (var i in data.results) {
var results = data.results[i];
var nameResults = results.name;
console.log(nameResults);
var typesResults = results.types;
console.log(typesResults);
var vicinityResults = results.vicinity;
console.log(vicinityResults);
};
// On button click, randomly display item from selected array
button.addEventListener('click', function () {
console.log('clicked');
display.style.display = 'block';
//display.innerHTML = nameResults[Math.floor(Math.random() * nameResults.length)];
display.innerHTML = nameResults.toString() + "<br />" + vicinityResults.toString();
});
console.log('It is working');
},
error: function (request, status, error) {
console.log('It is not working');
}
});
});
Right now I am getting a list of names when I request names which is a parameter in the JSON object. Is there a way to get it into an array so the I can randomly select one of the values from the array?
Use the push method and a non-local variable:
this.nameResults = this.nameResults ? this.nameResults.push(results.name) : [];
then reference that with the Math.random method:
this.randName = function(){ return this.nameResults[Math.random() * this.nameResults.length | 0] };
References
Using Math.random flexibly
JavaScript: fast floor of numeric values using the tilde operator
I have a weird issue that is baffling me. I have a model:
var Model = new Schema({
name: String,
variations: Array
});
The variations entry looks like this:
[ {code: '', price: '' }, {code: '', price: '' }]
I need to add a new field - say "color". So I am doing this to batch update:
Model.find().exec(function(err, products) {
if (!err) {
products.forEach(function(p) {
for(var i = p.variations.length - 1; i >= 0; i--) {
p.variations[i]['color'] = 'red';
// This shows all existing variations
// with the new color feed - correct
console.log(p.variations[i]);
}
p.save(function(err) {
if (!err) {
console.log("Success");
} else {
console.log(err);
}
});
});
}
});
However the "color" field is not set - if I go through again and comment out the p.variations[i]['color'] = 'red'; line then it does not show. I can't seem to figure out why it's doing this. I have an onSave event that is triggered correctly so it's saving. I also do not have any check on the variations structure - i.e. there is no code that only allows code and price. I'm obviously missing something but after a couple of hours I ran out of ideas.
When you modify the contents of an untyped Array field like variations, you need to notify Mongoose that you've changed its value by calling markModified(path) on the modified document or a subsequent save() call won't save it. See docs.
for(var i = p.variations.length - 1; i >=0; i--) {
p.variations[i]['color'] = 'red';
}
p.markModified('variations');
p.save(function(err) { ...
You have to use the set function to change a property. The reasoning behind that is that mongoose has to mark the field as modified in order to be saved to the database.
for(var i = p.variations.length - 1; i >=0; i--) {
p.variations[i].set({"color":"red", "code":"herr"});
// or
p.variations[i].set("color":"red");
p.variations[i].set("code":"herr");
}
An alternative would be to change the field's value the old way, without going trought the setter, then manually mark it as modified: p.markModified('variations');
In my opinion you should always use the setter since this is more readable. You can just pass a json object containing all your changes in parameter and it will safely update the fields that you really want to change.