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.
Related
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.
I have a problem with a Javascript array in IE11 mainly in the for loop.
Here is the code:
function onResponseReceived(responseArray) {
found = true;
var i;
for(i in responseArray) {
var item = responseArray[i];
if (item.field_number == '5') {
item.value = intlToUsDate(item.value);
console.log(item.value);
}
var inputSelector = '[name="input_' + item.field_number + '"]';
var dom_elms = document.querySelectorAll(inputSelector);
for (var e in dom_elms) {
var dom_elm = dom_elms[e];
if (dom_elm.type == 'radio' || dom_elm.type == 'checkbox') {
if (dom_elm.value == item.value && !dom_elm.checked) {
dom_elm.click();
continue;
}
} else {
dom_elm.value = item.value;
}
}
}
}
Here is the output in IE11 using the console.log:
"
i
d
"
:
"
1
8
4
1
"
,
Here is an the output for the same Javascript using Chrome:
field_number
:
"5"
form_id
:
"10"
id
:
"1839"
is_synced
:
"1"
lead_id
:
"2967"
value
:
"05/08/2018"
__proto__
:
Object
Basically it process the information correctly.
In IE11, how can I have the array be an object like in Chrome,FF or Edge?
Thank you,
Kevin
querySelectorAll returns not an array but a NodeList, and its documentation says:
Don't be tempted to use for...in or for each...in to enumerate the
items in the list, since that will also enumerate the length and item
properties of the NodeList and cause errors if your script assumes it
only has to deal with element objects.
Therefore instead of below code
for (var e in dom_elms) {
var dom_elm = dom_elms[e];
:
}
you should use for IE:
Array.prototype.forEach.call(dom_elms, function(dom_elm) {
:
});
I found the solution. I had to add the following code:
var responseArray = JSON.parse(responseArray);
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
Is there a way to style part of the text in a label - change color, boldness, size, etc?
Use HTML widget instead of Label. Then:
HTML label = new HTML();
label.setHtml("Brown <span class=\"brown\">fox</span>");
I was a little bored, and I thought I might be able to offer something useful, so, that said, I offer this:
function elemStyle(el, needle, settings) {
// if there's no 'el' or 'needle' arguments, we quit here
if (!el || !needle) {
return false;
}
else {
// if 'el' has a nodeType of 1, then it's an element node, and we can use that,
// otherwise we assume it's the id of an element, and search for that
el = el.nodeType == 1 ? el : document.getElementById(el);
// if we have a 'settings' argument and it's an object we use that,
// otherwise we create, and use, an empty object
settings = settings && typeof settings === 'object' ? settings : {};
// defining the defaults
var defaults = {
'class': 'presentation',
'elementType': 'span'
},
// get the text from the 'el':
haystack = el.textContent || el.innerText;
// iterate over the (non-prototypal) properties of the defaults
for (var prop in defaults) {
if (defaults.hasOwnProperty(prop)) {
// if the 'settings' object has that property set
// we use that, otherwise we assign the default value:
settings[prop] = settings[prop] || defaults[prop];
}
}
// defining the opening, and closing, tags (since we're using HTML
// as a string:
var open = '<' + settings.elementType + ' class="' + settings.class + '">',
close = '</' + settings.elementType + '>';
// if 'needle' is an array (which is also an object in JavaScript)
// *and* it has a length of 2 (a start, and stop, point):
if (typeof needle === 'object' && needle.length === 2) {
var start = needle[0],
stop = needle[1];
el.innerHTML = haystack.substring(0, start) + open + haystack.substring(start, stop) + close + haystack.substring(stop);
}
// otherwise if it's a string we use regular expressions:
else if (typeof needle === 'string') {
var reg = new RegExp('(' + needle + ')');
el.innerHTML = haystack.replace(reg, open + '$1' + close);
}
}
}
The above is called like so:
// a node-reference, and a string:
elemStyle(document.getElementsByTagName('label')[0], 'Input');
JS Fiddle demo.
// a node-reference, and a start-stop array:
elemStyle(document.getElementsByTagName('label')[0], [6, 8]);
JS Fiddle demo.
// an id (as a string), and a string to find, with settings:
elemStyle('label1', 'Input', {
'elementType' : 'em'
});
JS Fiddle demo.
This could definitely do with some error-catching (for example if an array is passed into the function that's less, or more, than two-elements nothing happens, and no error is returned to the user/developer; also if the el variable is neither a node-reference or an id, things go wrong: Uncaught TypeError: Cannot read property 'textContent' of null).
Having said that, I felt dirty, so I added in a simple error-check, and reporting, if the el doesn't resolve to an actual node in the document:
el = el.nodeType == 1 ? el : document.getElementById(el);
// if 'el' is null, and therefore has no value we report the error to the console
// and then quit
if (el === null) {
console.log("You need to pass in either an 'id' or a node-reference, using 'document.getElementById(\"elemID\")' or 'document.getElementsByTagName(\"elemTag\")[0].");
return false;
}
References:
document.getElementById().
JavaScript regular expressions.
Node.nodeType.
string.replace().
String.substring().
typeof variable.
I'm receiving this error using the following javascript code:
function tempTest(evt) {
alert(evt.currentTarget.id);
ct = document.getElementById(evt.currentTarget.id);
rslt = document.getElementById('rslt');
var props;
for (var prop in ct) {
if (ct.hasOwnProperty(prop)) {
propVal = ct[prop];
var propDat = prop + ' = ' + propVal;
props += propDat + '<br/>';
}
}
rslt.innerHTML = props;
}
This one has me puzzled. Any ideas?
Not all the properties of a HTML element are primitives. for example, parent, childs etc are also HTML elements. You can't just use them as strings or numbers.
You need to add there a condition and use that property accordingly.
If the object in question is json, you can call JSON.stringify(thingThatIsJson) which will return a String. .toString() does not work on json.
This is a message to those of you dealing with something like req.body which will work in console.log() which is rather confusing since it may not otherwise behave like a String (like when you're trying to add it to another String).
(The OP:)
Just wanted to post the updated snippet for anyone who stumbles onto this post...
function tempTest(evt) {
alert(evt.currentTarget.id);
ct = document.getElementById(evt.currentTarget.id);
rslt = document.getElementById('rslt');
var props;
for (var prop in ct) {
if (ct.hasOwnProperty(prop)) {
var propVal = ct[prop];
props += prop + ' (' + typeof(prop) + ')' + ' = ';
if (typeof(ct[prop]) == 'string') {
propVal += ct[prop];
} else {
if (propVal != null && propVal.toString) {
props += propVal.toString();
} else {}
}
props += '<br/>';
}
}
rslt.innerHTML = props;
}
The problem lies with the propVal part of your code. Since that may not be converted into a string.