jQuery how to traverse json object dynamically - javascript

i validate my formular with ajax and get back the following json object:
{"username":["Please enter a username"],"email":["Please enter an email"],
"plainPassword":{"first": ["Please enter a password"]},"firstname":
["This value should not be blank."],"lastname":["This value should not be blank."],
"terms":["This value should be true."],"privacy":["This value should be true."],
"captcha":["Code does not match"],"securityQuestion":["This value should not be blank."],
"plainSecurityAnswer":["This value should not be blank."],"intention":
["This value should not be blank."],"addresses":[{"state":["This value should not be blank."],
"city":["This value should not be blank."],"zipcode":["This value should not be blank."],
"country":["This value should not be blank."]}]}
The keys are mapped to the input fields id always by:
var id = "fos_user_registration_form_" + key;
I want to present these errors in an efficient way as tooltips to the fields. For that, i've written the following jQuery code (where callback is the returned json object):
$.each( callback, function( key, entry ) {
if(key != "addresses" && key != "plainPassword")
{
var id = "#fos_user_registration_form_" + key;
$(id).tooltip('destroy');
$(id).tooltip({'title': entry});
$(id).closest('div[class="form-group"]').addClass('has-error');
}else if(key == "addresses"){
$.each( entry[0], function( keyAddress, entryAddress ) {
var id = "#fos_user_registration_form_" + key + "_0_" + keyAddress;
$(id).tooltip('destroy');
$(id).tooltip({'title': entryAddress});
$(id).closest('div[class="form-group"]').addClass('has-error');
});
}else if(key == "plainPassword")
{
var id= "#fos_user_registration_form_plainPassword_first,#fos_user_registration_form_plainPassword_second";
$(id).tooltip('destroy');
$(id).tooltip({'title': entry.first});
$(id).closest('div[class="form-group"]').addClass('has-error');
}});
It is working, but i think not very dynamic because i know in this case that the entries of the key "addresses" and "plainPassword" aren't strings and that i have to iterate on them again (here only on addresses).
Is there a nicer way to do this by only using the key and entry variable of the loops, without knowing the "key" names of the json ?
I thought of something like: While entry !== "string", iterate as long threwthe entries as there is another array or object in it and build up the "id" variable. When there is a string field as "entry", use it as tooltip text.
Hope you guys can help me.
Regards.

Recursion will do this!
eg http://repl.it/3hK/5
Code -
var id_stub = "#fos_user_registration_form_"
// Here's the recursive function - we kick it off below.
function process(thing, id) {
var key
for (key in thing) {
// Handle the arrays
if ('length' in thing[key]) {
// Handle the end - we found a string
if (typeof thing[key][0] == "string") {
var html_id = id_stub + id + key
var err_msg = thing[key][0]
console.log(html_id, ":", err_msg)
// Now do your jquery using the html_id and the err_msg...
}
// Else we found something else, so recurse.
else {
var i = 0;
while (i < thing[key].length) {
process(thing[key][i], key + "_" + i + "_")
i++
}
}
}
// Handle the objects by recursing.
else {
process(thing[key], key + "_")
}
}
}
// Start the recursion from here.
process(callback, "")
I added an extra address to test how this code handles nested addresses, and using that I get this in the console:
#fos_user_registration_form_username : Please enter a username
#fos_user_registration_form_email : Please enter an email
#fos_user_registration_form_plainPassword_first : Please enter a password
#fos_user_registration_form_firstname : This value should not be blank.
#fos_user_registration_form_lastname : This value should not be blank.
#fos_user_registration_form_terms : This value should be true.
#fos_user_registration_form_privacy : This value should be true.
#fos_user_registration_form_captcha : Code does not match
#fos_user_registration_form_securityQuestion : This value should not be blank.
#fos_user_registration_form_plainSecurityAnswer : This value should not be blank.
#fos_user_registration_form_intention : This value should not be blank.
#fos_user_registration_form_addresses_0_state : This value should not be blank.
#fos_user_registration_form_addresses_0_city : This value should not be blank.
#fos_user_registration_form_addresses_0_zipcode : This value should not be blank.
#fos_user_registration_form_addresses_0_country : This value should not be blank.
#fos_user_registration_form_addresses_1_state : This value should not be blank.
#fos_user_registration_form_addresses_1_city : This value should not be blank.
#fos_user_registration_form_addresses_1_zipcode : This value should not be blank.
#fos_user_registration_form_addresses_1_country : This value should not be blank.
and sets up the variables you need to do your jQuery work.

function isValidationMessage(entry) {
return entry.length === 1 && typeof entry[0] === 'string';
}
function displayValidationMessage(key, message){
var id = "#fos_user_registration_form_" + key;
$(id).tooltip('destroy');
$(id).tooltip({'title': message});
$(id).closest('div[class="form-group"]').addClass('has-error');
}
function displayValidationMessageForArray(key, entries) {
for(var i = 0; i < entries.length; i++) {
$.each(entries[i], function(keyAddress, entryAddress) {
displayValidationMessage(key + "_i_" + keyAddress, entryAddress);
})
}
}
function displayValidationMessageForObject(key, entries) {
$.each(entries, function(entry, message) {
displayValidationMessage(key + "_" +entry, message);
})
}
function displayAllValidationMessages(callback) {
$.each( callback, function( key, entry ) {
if(isValidationMessage(entry)) {
displayValidationMessage(key, entry);
}else if($.isArray(entry)){
displayValidationMessageForArray(key, entry);
}else {
displayValidationMessageForObject(key, entry);
}
});
}
not fully tested, idea is to extract the if else to small function, and reuse them as much as possible

Related

localStorage is not saving values of similar names

My localStorage is only saving a few variables (3) of similar names, and then proceeding to override the last variable with the new value.
What I am trying to do is add variables into localStorage that have a name and a number attached to them such as Test1, Test2, Test3, etc.
The only issue is that after the third element, in this case Test3, the key gets overridden to Test4 and the value changes to the new value. This happens forever as long as the word Test is the same.
I can add other values just fine, but only up to 3 of the same root word.
This is the code I am using to add the elements:
const AddToLocalStorage = (type, contents) => {
let ind = 0;
Object.keys(localStorage).forEach(function (key) {
if (key == (type + ind)) {
ind++;
} else {
return;
}
});
localStorage.setItem((type + ind), JSON.stringify(contents));
}
type is a string such as Test
contents is the value stored
Thanks in advance :)
Edit - Can you clarify how to call the AddToLocalStorage function
AddToLocalStorage("Test", "value");
In localStorage this would set like { "Test0", "value" }
First of all, JavaScript Object Property order isn't guaranteed, therefore you're probably not receiving the keys in the same order as your browser shows them
That said, lets add some console.log's to debug the code:
const AddToLocalStorage = (type, contents) => {
let ind = 0;
console.log('Existing keys', Object.keys(localStorage));
Object.keys(localStorage).forEach(function (key) {
if (key == (type + ind)) {
ind++;
} else {
return;
}
});
console.log('Set', (type + ind))
localStorage.setItem((type + ind), JSON.stringify(contents));
}
AddToLocalStorage("Test", "value1");
On the second run, this logs:
Existing keys (3) ["Test2", "Test0", "Test1"]
Set Test2
Here we can see that (type + ind) will be Test2, because (key == (type + ind)) will be true for Test1 and Test2.
Since Test2 already exists, you'll override the existing value.

How do I open an alert window if the new value is duplicated in the existing value in JS?

Here, there is a global variable. (An array type)
var obj = [];
Gets the value entered in input.
var d_idx = $('#d_idx').val();
var d_firstDate = $('#firstDate').val();
var d_secondDate = $('#secondDate').val();
var chozyintime = $('#ri_chozyinTime').val();
var zaezyintime = $('#ri_zaezyinTime').val();
"chozyintime" and "zaezyintime" are declared as arrays.
Because you can input multiple values.
var chozyinArray = [chozyintime];
var zaezyinArray = [zaezyintime];
At this time, I gave the condition. I will explain it later.
first,
if(obj.length <= 0)
{
firstAddData(d_idx, d_firstDate, d_secondDate, chozyinArray, zaezyinArray);
}
If the size of the initial obj is zero, push the input value to obj.
firstAaddData function is :
function firstAddData(d_idx, d_firstDate, d_secondDate, chozyinArray, zaezyinArray)
{
obj.push
(
{
"d_idx" : d_idx,
"ri_firstDate" : d_firstDate,
"ri_secondDate" : d_secondDate,
"ri_chozyinTime" : chozyinArray,
"ri_zaezyinTime" : zaezyinArray
}
);
}
Second obj.length> = 1,
At this time, the conditions described above are set.
This is, Of the values of the first pushed obj,
d_firstDate, and d_secondDate are compared with the newly inputted d_firstDate and d_secondDate.
else if(obj.length >= 1)
{
var filterObj = obj.filter(function(cur)
{
return cur.ri_firstDate == d_firstDate && cur.ri_secondDate == d_secondDate;
})
if(filterObj.length > 0)
{
filterObj.forEach(function(cur, idx)
{
if(chozyintime != "" && chozyintime != null)
{
cur.ri_chozyinTime.push(chozyintime);
}
})
}
else
{
firstAddData(d_idx, d_firstDate, d_secondDate, chozyinArray, zaezyinArray);
}
}
As a result, I got the following output.
{ri_zaezyinTime=[1231,1421,2561], ri_firstDate=2017-12-15, ri_chozyinTime=[5212, 2314], ri_secondDate=2017-12-26, d_idx=1}
However, exception handling is not implemented for duplicate values.
When you add a new input value (chozyintime), Compared to the n elements in the array, I want to make the alert window pop up when there are duplicate values.
How should I write code to implement what I want to implement?
I need your feedback. Help.

Removing nulls from html popup

I would like to format the content of my popup so that null values are completely removed. At this point, my popup is filled with the features.properties array. There are 20 elements in properties, and depending on the queried feature, many of those values will be null.
var feature = features[0];
// Populate the popup and set its coordinates
// based on the feature found.
popup.setLngLat(feature.geometry.coordinates)
.setHTML('<div><b>' + feature.properties.city + '</div></b>' + '<div>'
+ feature.properties.course1 + '</div>' + '<div>'+
feature.properties.course2 + '<div>' + feature.properties.course3 + '</div>')
.addTo(map);
At this point, an example popup with some null values looks like this:
My aim is to eliminate null values and not to display them in the popup.
So far I've tried the JSON.stringify option instead of listing each element in separate <div> element.
function replacer(key, value) {
// Filtering out properties
if (value === "null" || value === null) {
return undefined;
}
return value;
}
JSON.stringify(feature.properties, replacer, "\t").replace(/\"/g, "").replace(/,/g, "")
This produces the desired result but then formatting is the problem.
The JSON object does not display well in a popup even when encasing it in <pre> tags, which produces:
I would like to know if there is a solution to format my popup so that it looks like the first image - but excludes null values. How can one do this is html by listing all of the property elements (course1, course2, course3, etc...) without producing a bunch of empty <div> s?
Here's one way using classic Javascript:
var features = {
properties: {
city: "Salzburg",
course1: "DCLead",
course2: "null",
course3: null,
field_1: "Hello"
}
};
function htmlFromProps(props, exclude) {
var html = "";
var i = 0;
for (p in props) {
if (props[p] && props[p] != "null" && exclude.indexOf(p) === -1) {
html += "<div>" + (i === 0 ? "<strong>" : "");
html += props[p];
html += (i++ === 0 ? "</strong>" : "") + "</div>\n";
}
}
return html;
}
popup.innerHTML = htmlFromProps(features.properties, ["field_1"]);
#popup {
width: 80%
}
<textarea id="popup"></textarea>
Use it by calling .setHTML(htmlFromProps(features.properties, [])) where the second argument is an array of fields you want to exclude.
You could try filtering your properties, see the example below:
var feature = {
properties: {
city: 'Salzburg',
course1: 'test',
course3: 'test3'
}
};
var html = Object
.keys(feature.properties)
.map(key => feature.properties[key])
.filter(value => value)
.map((value, i) => i === 0
? `<div><strong>${value}</strong></div>`
: `<div>${value}</div>`
)
console.log(html);
The crucial part is .filter(value => value) where filter makes sure that only truthy (non-null) values remain in the array.

Filter a store with array of values from one property with ExtJS

I'm trying to apply a constraint on combobox. It's half-working at the moment.
On the combobox, I have this listener:
[...]
listeners: {
'focus': function (combo, value) {
var orgComboVal = Ext.getCmp('Org1')
var getOrgValue = orgComboVal.getValue();
if (typeof getOrgValue !== undefined) {
reseaulist.clearFilter(true);
for (var q = 0, l = getOrgValue.length; q < l; q++) {
reseaulist.filter([
{property:'code_organisme', value: getOrgValue[q]}
]);
}
}
}
}
Ext.getCmp('Org1') defines another combobox.
When orgComboVal.getValue() is a single value, the filter is well applying.
but when it's an array of value, eg ["5", "9"], it's not working and the combobox supposed to be filtered shows no value (so I guess a filter is still applied but in an incorrect way).
I guess it's because the reseaulist.filter is called multiple time.
How can I achieve this ?
I saw the filterBy method but I don't know how to make it work.
Also, this post is interesting : How to filter a store with multiple values at once? but same, can't make it work since
getOrgValue.split(',')
is showing an error
(Object Array has no method split)
Any tips ? I'm using ExtJS 4.2.
EDIT
Thanks to #rixo, I've made it.
Also, I had to change some of the code he provided me, because the value of the Org1 combobox was always an array, even if empty, so the store filter was never cleared.
Here it is :
'focus': function (combo, value) {
var orgComboVal = Ext.getCmp('Org1')
var values = orgComboVal.getValue();
console.log(values)
if (values != null) {
reseaulist.clearFilter(false);
if (Ext.isArray(values)) {
if (0 < values.length) {
reseaulist.filterBy(function(record, id) {
return Ext.Array.contains(values, record.get('code_organisme'));
});
} else {
reseaulist.clearFilter(true);
}
}
}
}
Each filter is applied one after the other on the previously filtered data set, so your code implements a logical AND. That's why all values are filtered out...
Here's an example using filterBy to accept any value that is in your array:
function (combo, value) {
var orgComboVal = Ext.getCmp('Org1')
var values = orgComboVal.getValue();
if (values != null) {
store.clearFilter(false);
if (Ext.isArray(values)) {
store.filterBy(function(record, id) {
return Ext.Array.contains(values, record.get('code_organisme'));
});
} else {
record.get('code_organisme') === values;
}
} else {
store.clearFilter(true);
}
}
Or you could also use a regex with the filter method:
function (combo, value) {
var orgComboVal = Ext.getCmp('Org1')
var values = orgComboVal.getValue();
if (values != null) {
var filterValue = Ext.isArray(values)
? new RegExp('^(?:' + Ext.Array.map(values, function(value){return Ext.escapeRe(value)}).join('|') + ')$')
: values;
store.clearFilter(false);
store.filter('code_organisme', filterValue);
} else {
store.clearFilter(true);
}
}
Concerning your error, arrays indeed don't have a split method. Strings can be split into an array. Arrays, on their side, can be joined into a string...
Try This....
var getOrgValue = "5,9,4"; // array of value
reseaulist.filterBy(function(rec, id) {
return getOrgValue.indexOf(rec.get('code_organisme')) !== -1;
});

remove items from object if value is empty

So I'm taking a form and using serializeArray() to get all the forms data. There are 10 text inputs. What I am trying to do is skip the forms that return a empty result or forms that have "" as value. Here is what I came up with and it returns the index of the forms with "" or empty results correctly.
$("#" + forms).on("submit", function(event) {
var allData = $(this).serializeArray();
event.preventDefault();
for (var key in allData) {
if (allData[key].value === "") {
allData.splice(key, 1);
}
}
});
But when I add allData.splice(key, 1); it doesn't remove all the values with "" as a result. I basically want to remove any input that isn't going to have a value.
Also the structure of the objec is as follows.
[0]
name: "emailone",
value "some#email.com"
[1]
name: "passwordone",
value: "123asd"
[2]
name: "emailtwo",
value "another#email.com"
[3]
name: "passwordtwo",
value: "asd123"
[4]
name: "emailthree",
value ""
[5]
name: "passwordthree",
value: ""
That happens because you are altering the array while traversing it...
When you key is 4 and the value is '' you remove that element (succesfully) but when you splice the array it becomes a smaller one.. so the element at position 5 is now at 4. Your key variable is increased to 5 but now there is no element 5 in your array..
You need to traverse it backwards
$("#" + forms).on("submit", function(event) {
var allData = $(this).serializeArray();
event.preventDefault();
for (var key = allData.length-1; key >= 0 ; key--) {
if (allData[key].value === "") {
allData.splice(key, 1);
}
}
});
By splicing an array as you iterate over it, you can accidentally skip values - in this case, by removing a value at index four, you decrease the index of the following value by one. The loop than increments to five, and the value that started at the fifth index is skipped.
A few other answers have posted reasonable ways to work around this. If you're working with a newer version of JavaScript or with a library like jQuery or underscore, you could alternatively do this:
allData = allData.filter(function(e) {
return e.value !== "";
});
With jQuery, this can be written as
allData = $.grep(allData, function(e) {
return e.value !== "";
});
and in underscore:
allData = _.filter(allData, function(e) {
return e.value !== "";
});
var arrLength = allData.length, i, cleanArray = [], serializedArr;
for(i = 0; i < arrLength; i += 1{
if(allData[i] != ""){
cleanArray.push(allData[i]);
}
}
serializedArr = cleanArray.serializeArray();
You simply use delete to achieve your need,
for (var key in allData) {
if (allData[key].value === "") {
delete allData[key];
}
}
additionally you don't need explicitly get the value by this toallData[key].value, toallData[key] will simply return the value associated with the particular key.
DEMO
Updated:
for (var i=0; i < allData.length ; I++) {
if (allData[i].value === "") {
allData.splice(i, 1);
i--;
}
}
You should decrement the value of the index by one, every time you are removing an object from the array.

Categories

Resources