JavaScript Plugin, Node is Object, should be HTMLElement? - javascript

I have got the below (Simplified for this issue) plugin, the node value which is the value of options.container it an HTMLObject however when I do typeof on it Object is returned.
This is an issue as when i try to override this using dataset values, I detect it's type and replace it accordingly.
// Define option defaults
constructor(node) {
const _ = this;
var options = {
container: node,
};
(function() {
for( const data in node.dataset ) {
var key = (data.replace("map", "")).charAt(0).toLowerCase() + (data.replace("map", "")).substr(1),
value = node.dataset[data];
// Check the value has been set
if (value !== '') {
switch(typeof options[key]) {
case 'object':
options[key] = JSON.parse(value);
break;
case 'HTMLElement':
options[key] = value.GetElement();
break;
default:
options[key] = value;
break;
}
}
}
console.log(options);
}.call(_));
// Initialize the map
// this.init();
};
static init(node) {
return new this(node);
}
static initAll(node) {
var _ = this,
instances = [];
window.addEventListener('load', function () {
var maps = document.querySelectorAll('[data-map]');
var current = [];
var InitializeMap = function (e) {
instances.push(_.init(e)); // Setup new instance of this class for each map
};
// Keep a reference for each map
for (var i = 0; i < maps.length; i++) {
var dataset = maps[i].dataset;
if (current.indexOf(dataset.googleMap) === -1) {
current.push(dataset.googleMap);
InitializeMap(maps[i]);
}
}
});
return instances;
}
As you can see in the code below, I get an attribute called data.map.container, which contains the value of #map_container. So during that switch function I need to detect that the node value is an HTMLObject not an object.
For the record is I do console.log(options.container) then the HTMLElement is displayed in the log.
P.S. GetElement() simply takes the string and returns the HTMLObject, so #element would do getElementById and .element would do getElementByClassName. Just my version of jQuery's $().

typeof can not tell such specific object types, see possible return values: https://developer.mozilla.org/hu/docs/Web/JavaScript/Reference/Operators/typeof
You can use instanceof to check for whatever class you need.

Related

Can we specify a generic getter on an object in javascript?

I want to do:
properties.email.value without triggering an error like: Can't read 'value' of 'undefined'
However, I don't want to do:
properties.email && properties.email.value and I don't want to use an helper, something like: get(properties, 'email.value').
I really want to keep the syntax properties.email.value
I can solve this by doing:
Object.defineProperty(properties, 'email', {
get: () => properties.email && properties.email.value,
enumerable: true,
configurable: true
});
Now the getter is in charge of doing my safety check. Perfect.
But I also want to be able to do properties.name.value safely.
But as the properties object comes from the API (json), I don't know the full list of properties possible.
So, is there a way to use this "magical" get syntax for any prop access like: properties[ANYTHING].value ?
OK, I've got something like this.
But you must create properties that way.
Hope this help :)
var properties = {
phone : {
value: "123456789"
}
}
var handler = {
get: function(target, name) {
return target.hasOwnProperty(name) ? target[name] : {};
}
};
var new_properties = new Proxy(properties, handler);
console.log("phone.value = " + new_properties.phone.value);
console.log("email.value = " + new_properties.email.value);
new_properties.email = {
value: 1
};
console.log("email.value after assign = " + new_properties.email.value);
The document reference here.
Edited
Even if the original properties object is unknown, this kind of usage works as well.
You could use a Proxy and get known properties and a custom result for unknow properties.
For changing properties, you could take the same approach and set the value.
var properties = { email: { value: 'foo#example.com' } },
proxy = new Proxy(
properties,
{
get: function(target, prop, receiver) {
if (prop in target) {
return target[prop] && target[prop].value
} else {
return;
}
},
set: function(target, prop, value) {
if (prop in target) {
target[prop].value = value;
} else {
target[prop] = { value };
}
}
}
);
console.log(proxy.email);
console.log(proxy.bar);
proxy.email = '41';
console.log(proxy.email);
I can't believe I'm doing this...
var wordlength = 7;
var alphabet="abcdefghijklmnopqrstuvwxyz";
alphabet += alphabet.toUpperCase() + "0123456789_";
var alen = alphabet.length;
var buildWord = function(number){
if(number===0){
return '';
}
return alphabet[number%alen]+buildWord(Math.floor(number/alen));
};
var total = Math.pow(alen, wordlength);
for(var i = 1; i<total; i++){
var w = buildWord(i);
if(isNaN(w[0]) && Object.prototype[w]===undefined){
Object.prototype[w]={};
}
}

JavaScript find property in JSON object

I have a JSON object that looks a bit like this:
{
name: 'test',
details: {
description: 'This is the long description',
shortDescription: 'This is the short description (ironically longer than the description!)'
}
}
Obviously the real object is a lot more complicated than this example, but I have omitted the details because they will only complicate the question.
So, with this object, I have a function that tries to get the value of the property, it looks like this:
// Private function for matching fields
var _matchField = function (item, filter) {
// Our variables
var text = item[filter.field],
values = filter.expression.split(',');
// If we have any text
if (text) {
// Loop through our values
angular.forEach(values, function (value) {
console.log(text);
console.log(value);
// See if we have a match
if (text.toLowerCase().indexOf(value.toLowerCase()) > -1) {
// We have found a match
return true;
}
});
}
// We have found no matches
return false;
}
The issue is the line:
var text = item[filter.field],
If the property was just the name then item['name'] would work with the above object. But if I want to get the description; item['details.descrption'] doesn't work.
So I need a function that will allow me to specify a property name and it will find the property and return its value.
But before I try to write one, I was hoping there might be a simple solution that someone has come across.
you can write your custom function for this
function getProperty(json, field) {
if (json == null || field == null) {
return null;
}
var value = json;
var fields = field.split(".");
for (var i = 0; i < fields.length; i++) {
value = value[fields[i]];
if (value == null) {
return null;
}
}
return value;
}
check this plnkr example https://plnkr.co/edit/8Ayd9wnh1rJh1ycx5R1f?p=preview
You can split the reference to the object and use a function for getting the right nested object/value.
function getValue(o, p) {
if (typeof p === 'string') {
p = p.split('.')
}
return p.length ? getValue(o[p.shift()], p) : o;
}
var item = { name: 'test', details: { description: 'This is the long description', shortDescription: 'This is the short description (ironically longer than the description!)' } };
document.write(getValue(item, 'details.description'));
I solved this by creating this function:
// Private function to get the value of the property
var _getPropertyValue = function (object, notation) {
// Get all the properties
var properties = notation.split('.');
// If we only have one property
if (properties.length === 1) {
// Return our value
return object[properties];
}
// Loop through our properties
for (var property in object) {
// Make sure we are a property
if (object.hasOwnProperty(property)) {
// If we our property name is the same as our first property
if (property === properties[0]) {
// Remove the first item from our properties
properties.splice(0, 1);
// Create our new dot notation
var dotNotation = properties.join('.');
// Find the value of the new dot notation
return _getPropertyValue(object[property], dotNotation);
}
}
}
};

Array that can store only one type of object?

Is it possible to create an array that will only allow objects of a certain to be stored in it? Is there a method that adds an element to the array I can override?
Yes you can, just override the push array of the array (let's say all you want to store are numbers than do the following:
var myArr = [];
myArr.push = function(){
for(var arg of arguments) {
if(arg.constructor == Number) Array.prototype.push.call(this, arg);
}
}
Simply change Number to whatever constructor you want to match. Also I would probably add and else statement or something, to throw an error if that's what you want.
UPDATE:
Using Object.observe (currently only available in chrome):
var myArr = [];
Array.observe(myArr, function(changes) {
for(var change of changes) {
if(change.type == "update") {
if(myArr[change.name].constructor !== Number) myArr.splice(change.name, 1);
} else if(change.type == 'splice') {
if(change.addedCount > 0) {
if(myArr[change.index].constructor !== Number) myArr.splice(change.index, 1);
}
}
}
});
Now in ES6 there are proxies which you should be able to do the following:
var myArr = new Proxy([], {
set(obj, prop, value) {
if(value.constructor !== Number) {
obj.splice(prop, 1);
}
//I belive thats it, there's probably more to it, yet because I don't use firefox or IE Technical preview I can't really tell you.
}
});
Not directly. But you can hide the array in a closure and only provide your custom API to access it:
var myArray = (function() {
var array = [];
return {
set: function(index, value) {
/* Check if value is allowed */
array[index] = value;
},
get: function(index) {
return array[index];
}
};
})();
Use it like
myArray.set(123, 'abc');
myArray.get(123); // 'abc' (assuming it was allowed)

Avoiding duplication in object- Javascript

I have the next object:
var persons= {};
persons["Matt"].push("A");
persons["Matt"].push("B");
persons["Matt"].push("C");
And I want to know if the object contains the element which I try to insert.
E.g:
persons["Matt"].push("A"); /* The element A already exist... And I don't want duplicate elements.*/
Anybody know one way to make it?
EDIT WITH MORE DETAILS:
I have a the next code:
function insertIfNotThere(array, item) {
if (array.indexOf(item) === -1) {
array.push(item);
}
}
function EventManager(target) {
var target = target || window, events = {};
this.observe = function(eventName, cb) {
if (events[eventName]){
insertIfNotThere(events[eventName], cb);
}else{
events[eventName] = []; events[eventName].push(cb);
}
return target;
};
this.fire = function(eventName) {
if (!events[eventName]) return false;
for (var i = 0; i < events[eventName].length; i++) {
events[eventName][i].apply(target, Array.prototype.slice.call(arguments, 1));
}
};
}
I use your method for checking if the element with the content indicated exist. But... It push the element ever... I don't know what's happening...
First things first. When you do
persons= {};
you are creating a global property called persons and assigning an empty object to it. You might want a local variable here. So, change it to
var persons = {};
And then, when you create a new key in the object, by default, the value will be undefined. In your case you need to store an array. So, you have to initialize it like this
persons['Matt'] = [];
and then you can use the Array.prototype.indexOf function to find out if the item being added is already there in the array or not (it returns -1 if the item is not found in the array), like this
if (persons['Matt'].indexOf("A") === -1) {
persons['Matt'].push("A");
}
if (persons['Matt'].indexOf("B") === -1) {
persons['Matt'].push("B");
}
You can create a function to do this
function insertIfNotThere(array, item) {
if (array.indexOf(item) === -1) {
array.push(item);
}
}
var persons = {};
persons['Matt'] = [];
insertIfNotThere(persons['Matt'], 'A');
insertIfNotThere(persons['Matt'], 'B');
// This will be ignored, as `A` is already there in the array
insertIfNotThere(persons['Matt'], 'A');
Use indexOf to check for the existence of A. If it doesn't exist (is -1), add it to the array:
if (persons['Matt'].indexOf('A') === -1) {
persons['Matt'].push('A');
}

Recursive nested property creation in JavaScript

I am trying to recursively build an object with a tree of properties based on a MongoDB-ish selector "top.middle.bottom". There are some underscorejs helpers as well:
function setNestedPropertyValue(obj, fields, val) {
if (fields.indexOf(".") === -1) {
// On last property, set the value
obj[fields] = val;
return obj; // Recurse back up
} else {
var oneLevelLess = _.first(fields.split("."));
var remainingLevels = _.rest(fields.split(".")).join(".");
// There are more property levels remaining, set a sub with a recursive call
obj[oneLevelLess] = setNestedPropertyValue( {}, remainingLevels, val);
}
}
setNestedPropertyValue({}, "grandpaprop.papaprop.babyprop", 1);
Desired:
{
grandpaprop: {
papaprop: {
babyprop: 1
}
}
}
Outcome:
undefined
Helps and hints would be appreciated.
Instead of recursion I would choose for an iterative solution:
function setNestedPropertyValue(obj, fields, val)
{
fields = fields.split('.');
var cur = obj,
last = fields.pop();
fields.forEach(function(field) {
cur[field] = {};
cur = cur[field];
});
cur[last] = val;
return obj;
}
setNestedPropertyValue({}, "grandpaprop.papaprop.babyprop", 1);
EDIT
And here is another version thanks to the suggestions by Scott Sauyet:
function setPath(obj, [first, ...rest], val) {
if (rest.length == 0) {
return {...obj, [first]: val}
}
let nestedObj = obj[first] || {};
return {...obj, [first]: setPath(nestedObj, rest, val)};
}
function setNestedPropertyValue(obj, field, val) {
return setPath(obj, field.split('.'), val);
}
// example
let test_obj = {};
test_obj = setNestedPropertyValue(test_obj, "foo.bar.baz", 1);
test_obj = setNestedPropertyValue(test_obj, "foo.bar.baz1", 1);
// will output {"foo":{"bar":{"baz":1,"baz1":1}}}, while in the original version only "baz1" will be set
console.log(JSON.stringify(test_obj));
It's plain javascript
It only appends properties and will not override a top level object
setNestedPropertyValue() does not mutate the passed object (although keep in mind it only returns a shallow copy of the object, so some properties may be shared references between the original object and the new one)
I know this is old, but I needed exactly that kind of function and wasn't happy with the implementation, so here is my version:
function setNestedPropertyValue(obj, field, val) {
if (field.indexOf(".") === -1) {
obj[field] = val;
} else {
let fields = field.split(".");
let topLevelField = fields.shift();
let remainingFields = fields.join(".");
if (obj[topLevelField] == null) {
obj[topLevelField] = {};
}
setNestedPropertyValue(obj[topLevelField], remainingFields, val);
}
}
// example
let test_obj = {};
setNestedPropertyValue(test_obj, "foo.bar.baz", 1);
setNestedPropertyValue(test_obj, "foo.bar.baz1", 1);
// will output {"foo":{"bar":{"baz":1,"baz1":1}}}, while in the original version only "baz1" will be set
console.log(JSON.stringify(test_obj));
It's plain javascript
It only appends properties and will not override a top level object
setNestedPropertyValue() does not return the object so it is clear that it mutates the passed object
As mentioned by Jack in the question, I was not returning my object in the last line in the else statement. By adding this, it is now working:
obj[oneLevelLess] = setNestedPropertyValue( {}, remainingLevels, val);
return obj; // Add this line
}

Categories