I'm running into a maddening problem where I set a variable to point to a jQuery selector, such as: var foobar=jQuery(this); I then pass this variable to a function to be worked on. Let's simplify a little and say the function looks like this:
function SetFieldValue (selector) {
selector.val('test');
console.log ( selector );
console.log ( jQuery('#' + selector.attr('id')) );
}
In this situation if you assume that:
the selector is always a form element (and therefore val() is a valid operation)
the selector does resolve to a single dom element which has an 'id' attribute
You would then expect the two console.log statements to output the same result, right? Well I'm running into a situation where this condition only happens about 90% of the time.
In order to give more context I've created a short screencast demonstrating the problem:
SCREENCAST LINK
For reference purposes, here's the actual SetFieldValue code that is shown in the screencast:
function SetFieldValue ( domObject, value ) {
// as a safety function, check if a string representation of the domObject was passed in and convert it to a jQuery object if it was
if ( jQuery.type(domObject) === "string") {
console.log ("Value passed into SetFieldValue was a string representation so converting to jQuery object");
domObject = jQuery(domObject);
}
if ( jQuery.inArray (domObject.prop('tagName').toLowerCase(),['input' , 'select' , 'textarea']) >= 0 ) {
console.log ("setting to value attribute: " + value);
if ( domObject.hasAttr('id') ) {
domObject.val(value);
//jQuery('#' + domObject.attr('id')).val(value);
} else {
domObject.attr('value',value);
}
console.log ("Using jQuery ID it is set to: " + jQuery('#' + domObject.attr('id')).val() );
console.log ("Using jQuery selector variable it is set to: " + domObject.val() );
} else {
console.log ("setting to html attribute");
domObject.html( value );
}
return domObject;
}
Lets examine the code a bit.
First assigning back to a parameter is not a good practice adding a var at the start of your function would be a lot better, as scope can be lost.
//Suggestion change parameter to domItem
var domObject
Your missing an error handler for when the parameter is not String.
when identifying the type use
<VARNAME>.constructor.toString().match(/function (\w*)/)[1] === "<TYPE>"
It's more efficient and handles custom types.
No need for all the logic in assignment of value attribute. Any dom Object can be made to have a value attribute. also not sure why you are setting the val versus the value.
domObject.attr('value',value);
It is at this point that I can see your code could really use some documentation to help explain purpose
If you are explicitly only wanting to set value on Input fields and set value as innerhtml on non input fields then yes the logic would be needed but could be simplified to ... as the value doesn't need to be detected to overwritten.
if (jQuery.inArray (domObject.prop('tagName').toLowerCase(), ['input' , 'select' , 'textarea']) >= 0) {
domObject.attr('value',value);
} else {
domObject.html( value );
}
No Idea why you are returning the domObject out.
So a quick rewrite without the return and keeping most of the logic adding error handling results in
/*jslint sloppy: true*/
/*global jQuery*/
function SetFieldValue(domString, value) {
// as a safety function, check if a string representation of the domObjects was passed in and convert it to a jQuery object if it was
var domObjects, index;
//errorhandling
if (domString === undefined || domString === null) {
throw {error : "domString must have a value."};
}
if (domString.constructor.toString().match(/function (\w*)/)[1] !== "string") {
if (domString.constructor.toString().match(/function (\w*)/)[1].match(/HTML[a-zA-Z]*Element/) === null) {
throw {error : "domString expected to be String or domObjects"};
}
} else {
if (jQuery(domString).length === 0) {
throw {error : "domString does not resolve to a detectable domObjects."};
}
}
//errorhandling
//action
if (domString.constructor.toString().match(/function (\w*)/)[1].match(/HTML[a-zA-Z]*Element/)) {
//made as an array to normalize as jQuery returns an array allows code to be simplified
domObjects = [domString];
} else {
domObjects = jQuery(domString);
}
//given that domObjects are an array need to step through the array
for (index = domObjects.length - 1; index >= 0; index -= 1) {
if (
jQuery.inArray(
domObjects[index].tagName.toLowerCase(),
['input', 'select', 'textarea']
) >= 0
) {
if (domObjects[index].hasAttr('id')) {
domObjects[index].val(value);
} else {
domObjects[index].attr('value', value);
}
} else {
domObjects[index].html(value);
}
}
}
The above passes JSLint
I know I didn't provide enough context for people to really dig into this problem but I have in the end solved it. What was the issue? Well it was #Kobi who first asked is the DOM element's ID unique ... to which I happily reported it was. And it had been but in fact that WAS the problem. Jesus. It's always the obvious things that you then go onto overlook that get you in trouble.
Anyway, thanks for your patience and help.
Related
I'm getting the below error when I check the length of an array. What would be the correct approach?
main.js
if (drugPrice.mailPrice.rejectMessage.length !== 0 && Array.isArray(drugPrice.mailPrice.rejectMessage)) {
//code goes here
}
Error
TypeError: Cannot read property 'length' of undefined
Try swapping the order of the checks:
if (Array.isArray(drugPrice.mailPrice.rejectMessage) && drugPrice.mailPrice.rejectMessage.length !== 0) {
code goes here
}
Validate your data, swapping the condition may help but it won't prevent, some errors from happeing. For example Array.isArray(drugPrice.mailPrice.rejectMessage) will throw an error if drugPrice.mailPrice is undefined.
if (drugPrice.mailPrice
&& drugPrice.mailPrice.rejectMessage
&& drugPrice.mailPrice.rejectMessage.length !== 0
&& Array.isArray(drugPrice.mailPrice.rejectMessage)) {
// code goes here
}
var drugPrice = { mailPrice: { rejectMessage: {} } };
if (drugPrice.mailPrice
&& drugPrice.mailPrice.rejectMessage
&& drugPrice.mailPrice.rejectMessage.length !== 0
&& Array.isArray(drugPrice.mailPrice.rejectMessage)) {
console.log('success');
} else {
console.log('fail')
}
NOTE
Always validate your data. Don't assume that you'll always get the right data. When working with objects always validate them, as doing data.name, can break your app, if data is null or undefined. for example, given the following object.
const drugPrice = { mailPrice: null };
doing, throws an error.
const drugPrice = { mailPrice: null };
// throws an error, Cannot read property 'rejectMessage' of undefined
if (Array.isArray(drugPrice.mailPrice.rejectMessage)) {
}
to prevent that from happening, we need to check if the propery exists, like the following.
const drugPrice = { mailPrice: null };
console.log(drugPrice.mailPrice && Array.isArray(drugPrice.mailPrice.rejectMessage) || 'Price is null or undefined')
You do not really need to actually do the .length !== 0. You can simply do:
if (Array.isArray(A.B.C) && A.B.C.length) { // <-- order is important here
//...
}
.length would be evaluated as a boolean and it will give you the same result as checking with !==0
That being said however your paths are quite long so you probably would want to make sure they are valid. Meaning if drugPrice or mailPrice are falsey you would have an issue. So usually you would want to check on them as well. Since your question was about the array part I will skip those but just as FYI.
You can build your own path checker or if you use libraries like lodash/underscore etc they always have a handy get/has functions to check like this (with lodash):
if (_.has(drugPrice, 'mailPrice.rejectMessage.length'))
//...
}
Obviously do not use those libraries just for that but if you already have them those methods are quite handy. You can simply check each of the paths as well via:
if (A && A.B && Array.isArray(A.B.C) && A.B.C.length) {
//...
}
It just gets tedious if you have long object paths etc.
The problem in your code is that javascript checks array length before checking if the array is the type of the array. You should change the order in the if statement.
You can try with:
if (myArr && Array.isArray(myArr) && myArr.length !== 0) {
// your code
}
Now the code is executed in the right order.
The first condition checks if myArr is defined,
The second condition checks if myArr is the type of Array, you can also do this way:
if (myArr && myArr.push && myArr.length !== 0) {
// your code
}
The third condition checks if myArr is not empty.
So I have this code in my html that does a v-for loop over an array and then for each entry call a method with a parameter using a v-if to only show the option tag is it returns true.
Here is the html code snippet portion:
<template v-for="a in agencies">
<option v-if="agencyCk(a) === true" :value="a">${a}</option>
</template>
Here is the method from the Vue script area:
agencyCk: function(agency) {
// checked object keys for matching name
if (Object.keys(st.fullAgency).find(function(k) {
// if name matches agency
if (k === agency) {
var obj = st.fullAgency[k];
// loops through an array for each value
for (h in st.hosts) {
// if value matches object key's value
if (link === st.hosts[h]) {
return true;
} else {
return false;
}
}
}
}));
}
As it is right now, it seems to return all as false.
The agencyCk function didn't explicitly return anything. So the return value of the function would be undefined which is interpreted as false.
To see why, have a closer look at the if statement, it basically boils down to something like this:
if (Object.keys(...).find(...));
It has no body. The return statements you wrote is for the find function not for the agencyCk function.
I think what you want is something like:
return Object.keys(...).find(...) != undefined
I am programming in Polymer 1.0 and am trying to create an IF function to change the value of a property. My function is the following:
_searchButton: function(selectednamedropdown, selectedtypedropdown){
if (selectednamedropdown=="no_name_selected" && selectedtypedropdown=="no_type_selected"){
this.searchUsagesBtn = true
} else{
this.searchUsagesBtn = false
}
}
In my mind when selectednamedropdown is equal to "no_name_selected" and selectedtypedropdown is equal to "no_type_selected" the function should set searchUsagesBtn to true and when they are not these values, false.
However, the function does not ever seem to be returning true even when these conditions are met. Any ideas why this might be? Thanks for all help
When I run your function like this:
let searchUsagesBtn;
function search(selectednamedropdown, selectedtypedropdown) {
if (
selectednamedropdown === "no_name_selected" &&
selectedtypedropdown === "no_type_selected"
) {
searchUsagesBtn = true;
} else {
searchUsagesBtn = false;
}
}
search("no_name_selected", "no_type_selected");
console.log("button: ", searchUsagesBtn);
I get button: true in console log. So maybe your inputs in this function are not a strings.
The issue was around how JavaScript treats properties within functions. The function was storing the new value and old value of the first property and not any values of the second property. The solution involved making 2 functions to test the strings in each property. Thanks for all assistance
I have a fullcalendar where I display non-editable events which are collected from a google calendar, or from a database. Then I want to register customer requests for events from the calendar. This works, but I am not able to list only the events that are added by the user.
Any hint on how to do this?
I tried this:
function retrieve_events() {
var rdv=$('#calendar').fullCalendar( 'clientEvents', undefined);
for (i=0; i<=rdv.length-1; i++) {
/*alert(rdv.toSource());*/
alert(rdv[i].title+" id: "+rdv[i].id+" start: "+rdv[i].start+" end:"+rdv[i].end+" heldag:"+rdv[i].allDay);
}
}
The the "undefined" as id, means that I have given all the non-editable events an id, while the new ones haven't got one. But this way I get all events listed, even those without an id. The same happens with null and ''. But using hardcoded id-numbers returns that specific event.
I see from the documentation that there seems to be other ways to get hold of the events I need, by using other criteria like classes. However I cannot figure out how to specify this filter.
I haven't worked with FullCalendar yet nor do I intend to extensively test this, so I cannot guarantee that this will work.
However, why don't you simple test whether rdv[i].id evaluates to false?
Try:
function retrieve_events( ) {
var rdv = $('#calendar').fullCalendar('clientEvents'),
results = [];
for( var i = 0; i < rdv.length; ++i ) {
if( !rdv[i].id ) {
results.push(rdv[i]);
}
}
return results;
}
P.S.: Passing undefined to .fullCalendar() probably is redundant. It would be equivalent to passing only a single variable. I'd guess the second parameter is a type of events that you can filter for, but passing only a single parameter would cause the plugin to return all events. Also, note that !!'' === false.
The internal check whether the second parameter is set is probably similar to this:
$.fn.fullCalendar = function( command ) {
switch( command ) {
// ... some case's
case 'clientEvents':
var filter = arguments[1];
if( !filter ) {
// Retrieve ALL client events
}
else {
// Filter client events
}
break;
// ... some more case's
}
};
This does not compare types. Testing filter === false would only return true, if filter would evaluate to false and is a boolean.
Following are examples of values that evaluate to false. There may be more, but I believe those are all.
undefined
null
0
false
''
I've just been working on a ExtJS script and I have a ComboBox which has
allowBlank = false
and
forceSelection = true
I have an item in the list which acts as a default message which has a display text
Please select...
and no value
''
When I run validate on the ComboBox I get true
No idea why?
According to the documentation when
allowBlank = false
the validation is forced to check for value.length > 0
So I have done my own test in the JS Console
>> if (thisForm.controlManager.controlArray[2].allowBlanks) { if (thisForm.controlManager.controlArray[2].length >= 0) { true; } false; } else { if (thisForm.controlManager.controlArray[2].length > 0) { true; } false; }
and it returned false
So I thought it might a bug in validate method so I tried doing this
>> thisForm.controlManager.controlArray[2].validateValue('')
and got this as a result true
Any one have any kind of idea of what I might be doing wrong or if anything else needs set to get this validate to return false when value is ''.
PS. I've also tried this
>> thisForm.controlManager.controlArray[2].validateValue(' ')
and got the correct result which is false. This made me very confused as I would normally expect '' and ' ' to return the same value in validation.
I know that a workaround would be to set my value to ' ' but I would rather get it working with ''.
Thanks
I just so happened to end up grappling with this same issue, and after some looking around, managed to find a solution which does not require overriding Extjs's standard functionality.
Basically, there is a 'validator' config option for descendents of Ext.form.field.Text which allows programmers to specify a custom validation function for a component (see here).
Basically, your validator function gets called at the start of getErrors() and is evaluated before the rest of the field's standard validation. The validator function takes one argument (the value) and must return either true if the value is valid or an error message string if it is not.
The following config ended up working for my case:
validator: function (value) {
return (value === '/*Your emptytext text*/') ? "blankText" : true;
}
You have to use the emptyText configuration
Ext have this code for validate fields:
validate : function(){
if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
this.clearInvalid();
return true;
}
return false;
}
and getRawValue is defined like this:
getRawValue : function(){
var v = this.rendered ? this.el.getValue() : Ext.value(this.value, '');
if(v === this.emptyText){
v = '';
}
return v;
}
so, if the value is equal to the empty text, the returned value is ''