I am trying to post an object only if it's not empty. However I have code which causes properties to become undefined -- and when that happens, the obj is not empty anymore and the post still happens.
userSearchData = {};
$('#addContactForm').keyup(function()
{
var email = $(this).find('input[name="email"]').val();
var username = $(this).find('input[name="username"]').val();
var fullName = $(this).find('input[name="fullName"]').val();
userSearchData.email = email.length >= 3 ? email : undefined;
userSearchData.username = username.length >= 3 ? username : undefined;
userSearchData.fullName = fullName.length >= 3 ? fullName : undefined;
console.log(userSearchData);
if ( !isEmpty(userSearchData) )
{
console.log("Is empty")
$.post( '/post/addContact', { userSearchData: userSearchData }, function( data )
{
console.log( data );
});
}
});
It's a "search" form, so if a user types for example "Blabla" as the username, and then erases letters to make it "Bl", then the username variable gets undefined, so it's not being sent when doing the post (I console log the object on the server side and the undefined variables are not considered which is good).
How can I make my variables completely removed, instead of undefined when their length is below 3?
I could probably modify the isEmpty function to return false if all keys are undefined, would that be better to do that? If so, how would you do it?
var hasOwnProperty = Object.prototype.hasOwnProperty;
function isEmpty (obj)
{
// null and undefined are "empty"
if (obj == null) return true;
// Assume if it has a length property with a non-zero value
// that that property is correct.
if (obj.length > 0) return false;
if (obj.length === 0) return true;
// Otherwise, does it have any properties of its own?
// Note that this doesn't handle
// toString and valueOf enumeration bugs in IE < 9
for (var key in obj) {
if (hasOwnProperty.call(obj, key)) return false;
}
return true;
}
The whole thing seems rather pointless, you can just do this instead
$('#addContactForm').on('keyup', function() {
var userSearchData = {}, self = this;
$.each(['email', 'username', 'fullName'], function(_, el) {
var val = $(self).find('input[name="'+el+'"]').val();
if ( val.length > 3 ) userSearchData[el] = val;
});
$.post( '/post/addContact', { userSearchData: userSearchData }, function( data ) {
console.log( data );
});
});
Only add the properties to the object if the condition is met.
if ( username.length >=3 ) {
userSearchData.username = username;
}
if ( username in userSearchData ) {
// do stuff
}
you can delete properties in JS, but the better fix is to just make sure your code posts when it should.
if (obj === null || obj === undefined) return;
or something might help you here.
Also, for(key in obj) is old-style "iterate over prototype as well", and highly discouraged, so you probably want this instead:
var keys = Object.keys(obj);
if(keys.length === 0) ...
keys.forEach(function(key) { ... });
Do you mean you want do this?
if(!isEmpty(userSearchData)){
$.post( '/post/addContact', { userSearchData: userSearchData }, function( data )
{
console.log( data );
});
}
Related
I have a method that takes a language abbreviation and matches it using a .constant dictionary, and returns the matching language name.
How can I do an evaluation with .filter to check whether the passed isoCode/language abbreviation exists?
Here is my method:
angular.module('portalDashboardApp')
.service('ISOtoLanguageService', ['Languages', function(Languages) {
this.returnLanguage = function(isoCode) {
var categoryObject = Languages.filter(function ( categoryObject ) {
return categoryObject.code === isoCode;
})[0];
return categoryObject.name;
};
}]);
Here is the method with some error catching I have tried:
angular.module('portalDashboardApp')
.service('ISOtoLanguageService', ['Languages', function(Languages) {
this.returnLanguage = function(isoCode) {
var categoryObject = Languages.filter(function (categoryObject) {
if (isoCode != null || isoCode != undefined) {
return categoryObject.code === isoCode;
}
else {
return categoryObject.code === 'und';
}
})[0];
if (categoryObject.name != undefined || categoryObject.name != null) {
return categoryObject.name;
}
else {
return "undefined";
}
};
}]);
Thank you!
I would recommend you organize your data at Languagesin an object or map, it'll be much faster and simpler when you fetch your translation by an abbreviation. A short example:
angular.module('portalDashboardApp')
.factory('Languages', function(){
var dictionary = {
ISO: {name: 'International Organization for Standardization'}
};
return {
get: function(abbr){
return dict[abbr];
}
};
}).service('ISOtoLanguageService', ['Languages', function(Languages) {
this.returnLanguage = function(isoCode) {
if(!isoCode) {
return "Answer for empty isoCode";
}
var categoryObject = Languages.get(isoCode);
return (categoryObject || {}).name || "I don't know this abbr";
};
}]);
I'm not sure that this JS works without any syntax error (I've not try to launch it) but idea is that you don't need array and filter on big dictionaries and you are able to get any abbreviation from dict with O(1) complexity even with huge dictionary.
If you don't want to have a refactoring with your code you can do something like this:
angular.module('portalDashboardApp')
.service('ISOtoLanguageService', ['Languages', function(Languages) {
this.returnLanguage = function(isoCode) {
if (!isoCode) {
return;
}
var resultAbbrs = Languages.filter(function (categoryObject) {
return categoryObject.code === isoCode;
});
if (resultAbbrs.length > 0) {
return resultAbbrs[0].name;
}
};
}]);
In this case if isoCode is null, undefined or empty string or this key is not found in dictionary return undefined will be by default. Outside you should check a result of this function with if (result === undefined) ...
I hope it helped you)
I'm a js developer and work in an environment where we do API calls and get some data returned. The structure of the data returned is HIGHLY inconsistent so therefor we can't assume anything about the data returned.
Picture the following scenario:
$.ajax({
success: function(data){
// Now I want to access a property 4 levels in
var length = data.results.users.length;
doSomeStuffWithLength(length);
}
})
What's the correct way to ensure data.results.users.length is not undefined? Because of the inconsistencies from the API, each level of the returned object could be broken/undefined. Do I really have to do the following:
if (data && data.results && data.results.users && data.results.users.length){
var length = data.results.users.length;
doSomeStuffWithLength(length);
}
Aren't there more elegant solutions?
You can create helper function like this.
Expect object with structure like this :
var someObj = {
some: {
other: {
third: 'bingo',
qwe: 'test'
}
}
};
Would be great to have something like
getPropByPath(someObj, 'some.other.qwe');
So the implementation of getPropByPath may looks like following:
function getPropByPath(obj, path){
var parts = path.split('.'),
root = obj;
for(var i=0; i<parts.length; i++) {
if(root[parts[i]] !== 'undefined') {
root = root[parts[i]]
} else {
return false;
}
}
return root;
}
If at all levels there may be something undefined, you should check all levels, something like:
var length = data &&
data.results &&
data.results.users &&
data.results.users.length || 0;
You can also use some helper function. Here's one:
function getPropValue ( path, defaultIfNotExisting ) {
defaultIfNotExisting = defaultIfNotExisting || 0;
path = path.split('.'), test = this;
while (path.length) {
test = test[path.shift()];
if (!test) {
return defaultIfNotExisting;
}
}
return test;
}
// usage in your case:
if ( getPropValue.call(data, 'results.users', []).length) { /* do stuff */}
I have a form with inputs using this naming convetion:
<input class="xxlarge" name="note[url]" id="url" placeholder="URL">
So, I'm using this script (found on StackOverflow) that serializes form data into JSON.
$.fn.serializeObject = function()
{
var o = {};
var a = this.serializeArray();
$.each(a, function() {
if (o[this.name] !== undefined) {
if (!o[this.name].push) {
o[this.name] = [o[this.name]];
}
o[this.name].push(this.value || '');
} else {
o[this.name] = this.value || '';
}
});
return o;
};
and on the output I have this:
{"note[url]":"URL","note[title]":"TITLE"}
I'd like to know how to transform this script to get output like this:
{"url":"URL","title":"TITLE"}
I'm handling this from with rather standard, documented code block (using function, described above):
$(function() {
$('form').submit(function() {
$('#result').html(JSON.stringify($('form').serializeObject()));
$.post(
"/api/create",
JSON.stringify($('form').serializeObject()),
function(responseText){
$("#result").html(responseText);
},
"html"
);
return false;
});
Thanks in advance!
I would suggest parsing the string into a JS object, changing the keys in a for loop, then stringifying it when you're done. Like so:
// turn the string into a JS object
var data = JSON.parse('{"note[url]":"URL","note[title]":"TITLE"}');
var newData = {};
// step through each member
for(key in data) {
// Regular expressions to find the brackets
var newKeyStart = key.search(/note\[/) + 5;
var newKeyEnd = key.search(/\]/);
// pull out the desired part of the key
var newKey = key.substr(newKeyStart, newKeyEnd - newKeyStart);
// insert into new data object
newData[newKey] = data[key];
}
// turn back into JSON again
var newJSON = JSON.stringify(newData);
Not sure where your 'note' part is coming from. May be something you could fix via the name attributes in your markup. Otherwise you could always do something like:
function renameKeys(obj) {
var
result = {},
key,
check,
noteReg = /^note\[([^\]]+)\]$/;
for(key in obj) {
result[(check = key.match(noteReg)) === null ? key : check[1]] = typeof obj[key] == 'object' && toString.call(obj[key]) == '[object Object]' ? renameKeys(obj[key]) : obj[key];
}
return result;
}
which can be used to make a new object with the keys you want.
renameKeys({"note[url]":"URL","note[title]":"TITLE"});
// { url: 'URL', title: 'TITLE' }
renameKeys({"note[url]":"URL","note[title]":"TITLE", anotherObj: { thingA: 1234, 'note[thingB]': 9492}});
// { url: 'URL', title: 'TITLE', anotherObj: { thingA: 1234, thingB: 9492 } }
Beware, though, that if you have something like a key of note[asdf] and a key of asdf then whichever is iterated over last will overwrite the other.
From the node REPL thing,
> d = {}
{}
> d === {}
false
> d == {}
false
Given I have an empty dictionary, how do I make sure it is an empty dictionary ?
function isEmpty(obj) {
return Object.keys(obj).length === 0;
}
You could extend Object.prototype with this isEmpty method to check whether an object has no own properties:
Object.prototype.isEmpty = function() {
for (var prop in this) if (this.hasOwnProperty(prop)) return false;
return true;
};
How about using jQuery?
$.isEmptyObject(d)
Since it has no attributes, a for loop won't have anything to iterate over. To give credit where it's due, I found this suggestion here.
function isEmpty(ob){
for(var i in ob){ return false;}
return true;
}
isEmpty({a:1}) // false
isEmpty({}) // true
This is what jQuery uses, works just fine. Though this does require the jQuery script to use isEmptyObject.
isEmptyObject: function( obj ) {
for ( var name in obj ) {
return false;
}
return true;
}
//Example
var temp = {};
$.isEmptyObject(temp); // returns True
temp ['a'] = 'some data';
$.isEmptyObject(temp); // returns False
If including jQuery is not an option, simply create a separate pure javascript function.
function isEmptyObject( obj ) {
for ( var name in obj ) {
return false;
}
return true;
}
//Example
var temp = {};
isEmptyObject(temp); // returns True
temp ['b'] = 'some data';
isEmptyObject(temp); // returns False
I'm far from a JavaScript scholar, but does the following work?
if (Object.getOwnPropertyNames(d).length == 0) {
// object is empty
}
It has the advantage of being a one line pure function call.
var SomeDictionary = {};
if(jQuery.isEmptyObject(SomeDictionary))
// Write some code for dictionary is empty condition
else
// Write some code for dictionary not empty condition
This Works fine.
If performance isn't a consideration, this is a simple method that's easy to remember:
JSON.stringify(obj) === '{}'
Obviously you don't want to be stringifying large objects in a loop, though.
You'd have to check that it was of type 'object' like so:
(typeof(d) === 'object')
And then implement a short 'size' function to check it's empty, as mentioned here.
If you try this on Node.js use this snippet, based on this code here
Object.defineProperty(Object.prototype, "isEmpty", {
enumerable: false,
value: function() {
for (var prop in this) if (this.hasOwnProperty(prop)) return false;
return true;
}
}
);
$.ajax({
url : uri,
type : 'post',
data : {someBooleanVar1: false, subVar: {someBooleanVar2: true}}
});
The problem is that on server someBooleanVar1 and someBooleanVar2 will be received as strings "false" and "true", but not as "0" and "1". Is there any way to automatically convert boolean arguments to "1" and "0"?
There is a fixed version of #jcubic Answer:
function convertBoolToNum(obj) {
$.each(obj, function(i) {
if (typeof obj[i] == 'object') {
convertBoolToNum(this);
}
else if (typeof obj[i] == 'boolean') {
obj[i] = Number(obj[i]);
}
});
}
$.ajax = (function($ajax) {
return function(options) {
convertBoolToNum(options.data);
return $ajax(options);
};
})($.ajax);
Try this, it should automatically convert booleans values to numbers in data options.
$.ajax = (function($ajax) {
return function(options) {
if (options.data != undefined) {
for (var i in options.data) {
if (options.data.hasOwnProperty(i) &&
(typeof options.data[i] == "boolean")) {
options.data[i] = Number(options.data[i]);
}
}
}
return $ajax(options);
};
})($.ajax);
i know this post is a bit old but i still wanted to pass this information ^^
i pass vars to php and catch them with:
filter_var($var, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
like this i turn strings into booleans true=1 and false=
false as string is empty in php
maybe i read the question wrong. but this is what i understood :)
with the code above you can easy build a function and add more filters to get everything work as you want :)
Not really automatically but adding 0 will convert the boolean to 0 or 1:
data : {someBooleanVar1: false + 0, someBooleanVar2: true + 0}
This is a fix for Yi Jiang's answer. If you call ajax and don't have data set it gives an error. Added a test to see if the data property is set before converting the booleans. I used underscore, feel free to swap it out with the native js function hasownprop...
function convertBoolToNum( obj ) {
$.each( obj, function( i ) {
if ( typeof obj[i] == 'object' ) {
convertBoolToNum(this);
}
else if ( typeof obj[i] == 'boolean' ) {
obj[i] = Number( obj[i] );
}
} );
}
$.ajax = ( function( $ajax ) {
return function( options ) {
if ( _.has( options, 'data' ) ) {
convertBoolToNum( options.data );
}
return $ajax( options );
};
} )( $.ajax );
Here's an even more optimized version that doesn't pollute the global namespace, converts values recursively, takes care of the potential undefined data property, and uses the proper method for converting boolean values to their corresponding integer values.
$.ajax = (function($ajax) {
return function(options) {
(function (obj) {
var _fn = arguments.callee;
$.each(obj, function(i) {
if (typeof obj[i] == 'object') {
_fn(this);
} else if (typeof obj[i] == 'boolean') {
obj[i] = ~~obj[i];
}
})
})(options.data || {});
return $ajax(options);
};
})($.ajax);
I still think it's quite shocking that this isn't performed internally by jQuery before it sends the request.