Javascript - Overriding property (not methods) inside an Object - javascript

Let us explain the question with an example. I have a text box. The textbox (every textbox) has a property called 'value'. I want to over ride that textbox.value and comeup with and
new thing. When the text in textbox is 'ranjan' then the textbox.VALUE property returns 'ranjan'. Now I want to thus overwrite this so that when you type textbox.VALUE you get a different thing say for example, RaNjAn or say, Mr. Ranjan or whatever.
We can over ride methods using Object.PROTOTYPE property. But how can we do it for non-function objects inside object for example the 'value' property in this case.
If i need to make the question more clear, please mention.
Regards - Ranjan.

You can define custom properties for your element using Object.defineProperty
If you have a case where you need to get the value of an element as Mr. <value> for example, then this approach will be useful. Overriding standard properties may not be such a good idea.
Demo: http://jsfiddle.net/zvCGw/2/
Code:
var foo = document.getElementById('foo');
Object.defineProperty(foo, "xvalue", {
get: function() {
return 'Mr. ' + foo.value;
},
set: function(_newValue) {
foo.value = _newValue;
}
});
foo.xvalue = 'Hello';
alert(foo.xvalue);

What you are trying to do is called type augmentation. In javscript there are types of things, such as the object type, array type, etc.
You can use the prototype to augment these built in types, for example, adding a new method that can be called on any object that is of the type array:
Array.prototype.myNewMethod = function() {
//the method logic
}
Then you can call your method on any array:
[0,1,2].myNewMethod();
There is no INPUT type in JavaScript, DOM elements are classed as Objects. But you could jerry-rig something together that kind of does what you need, like this
Object.prototype.changeValue = function(el) {
if (el.tagName === "INPUT") {
return "Mr " + el.value;
}
}
var testEl = document.getElementById("test");
document.write(testEl.changeValue(testEl))
Used in conjunction with this textbox:
<input id="test" value="Dan" />
You would then get the output 'Mr Dan'
However, this is not great, it's just to illustrate the point and is just something to get you started...
I made a fiddle so you can play around with it

You can redeclare value but it will do no good ;)
This example would do that if test is a textbox
var input = document.getElementById("test");
Object.defineProperty(input, "value", {
get : function () {
return "'" + this["value"] + "'";
},
set : function (val) {
this["value"] = val;
}
});
input.value = "Hello World";
alert(input.value);
Unfortunately, "this.value" will reference the getter causing infinite recursion.
Once redefined, the original value will no longer exist so you will have crippled the element object.
At least as far as I have been able to test.

If the property you're trying to override can also be represented by an HTML attribute (e.g. an input's value), then you can use getAttribute and setAttribute.
Object.defineProperty(myInputElement, 'value', {
get: function () {
return myInputElement.getAttribute('value');
},
set: function (value) {
myInputElement.setAttribute('value', value);
}
});
Note, however, that this override itself cannot be overridden without re-implementing it.

Related

How do I set a JavaScript object's value to null

I have created this JS object from an array.
var rv = {};
$( ".part-name:visible" ).each(function( index ) {
//rv[$(this).text()] = arrayPartsName[$(this).text()];
rv[$(this).text()] = arrayPartsName[$(this).text()];
console.log(rv);
})
4GN: "4GN"
4GNTS: "4GNTS"
042645-00: "042645-00"
503711-03: "503711-03"
573699-05: "573699-05"
I have to use this object with Materialize Autocomplete and I have to edit it. The correct object must be, for example, like this
4GN: null
4GNTS: null
042645-00: null
503711-03: null
573699-05: null
How can do this?
Picking up from my comment. You can just set it to null ;) JavaScript is quite a cool language... you can pretty much set any object's properties to anything you want, null, a specific value, or even a function... see some more on the topic
But to focus on your specific question:
Change this line
rv[$(this).text()] = arrayPartsName[$(this).text()];
to
rv[$(this).text()] = null;
Something to be aware of
If you have property or key values in the JSON object with a dash in the name, you have to wrap it in quotes ", otherwise it wont be seen as valid. Although this might not be as evident, or an issue in your example as your keys are being added via the following function $(this).text().
var fruit = {
"pear": null, // something null
"talk": function() { console.log('WOOHOO!'); } // function
}
var apple = "app-le";
fruit[apple.toString()] = 'with a dash';
fruit["bana-na"] = 'with a dash';
// below is not allowed, the values will be evaluated as
// properties that dont exist, and then your js will fail
// fruit[pe-ar] = 'with a dash';
fruit.talk();
console.log(fruit);

Adding meta data to a primitive in javascript

Background
We have much of our data formatted like
var X = {value:'some val',error:'maybe an error',valid:true}
as a result we find ourselves calling X.value ALL the time.
We don't use the .error or .valid nearly as much, but we do use it.
What I want
To quit calling .value everywhere, but to still have access to meta data on a per data point level.
The Question
Is there one of
A) A way to put meta data on a primitive? attaching .error to an int for example? Is it possible for bools or strings?
B) A way to make a class that can be treated as a primitive, providing a specific data member when I do? IE X.value = 5, X+3 returns 8.
C) A better design for our data? Did we just lay this out wrong somehow?
You can set the method toString() to your object and return value.
var X = {
value: 1,
error:'maybe an error',
valid:true,
toString: function() {
return this.value;
}
}
X.value = 5;
console.log(X+3);
You can represent you data as a function object that also has properties:
var X = () => 1;
X.value = 1;
X.error = 'maybe an error';
X.valid = true,
console.log(X()); // 1
console.log(X.valid); // true
For better design you can encapsulate the creation of the data object in another function.

How can I compare a string to an object key and get that key's value?

I want to do something relatively simple, I think anyways.
I need to compare the pathname of page with an object's kv pairs. For example:
if("pathname" === "key"){return value;}
That's pretty much it. I'm not sure how to do it in either regular Javascript or jQuery. Either are acceptable.
You can see my fiddle here: http://jsfiddle.net/lz430/2rhds1x3/
JavaScript:
var pageID = "/electrical-electronic-tape/c/864";
var pageList = [{
"/electrical-electronic-tape/c/864": "ElectronicTape",
"/industrial-tape/c/889": "IndustrialTape",
"/sandblasting-tape/c/900": "SandblastingTape",
"/Foam-Tape/c/875": "FoamTape",
"/double-coated-d-c-dhesive-tape/c/872": "DCTape",
"/Adhesive-Transfer-Tape/c/919": "ATTape",
"/Reflective-Tape/c/884": "ReflectiveTape",
"/custom-moulding": "CustomMoulding",
"/request-a-quote": "RequestQuote"
}];
var label = pageID in pageList;
$('.el').html(label);
First, your "pageList" should just be a plain object, not an object in an array:
var pageList = {
"/electrical-electronic-tape/c/864": "ElectronicTape",
"/industrial-tape/c/889": "IndustrialTape",
"/sandblasting-tape/c/900": "SandblastingTape",
"/Foam-Tape/c/875": "FoamTape",
"/double-coated-d-c-dhesive-tape/c/872": "DCTape",
"/Adhesive-Transfer-Tape/c/919": "ATTape",
"/Reflective-Tape/c/884": "ReflectiveTape",
"/custom-moulding": "CustomMoulding",
"/request-a-quote": "RequestQuote"
};
Then you can set "label" to the value from the mapping:
var label = pageList[pageID] || "(not found)";
That last bit of the statement above will set the label to "(not found)" if the lookup fails, which may or may not be applicable to your situation.
It depends kinda on the logic you want to implement. If you want to say "if object has the key, then do X, and if not, then do Y", then you handle that differently than "set label to the object's key's value if the key is there, or else set it to undefined or something else".
For the first case you do:
if (pageList.hasOwnProperty(pageID) ) {
label = pageList[pageID];
}
else {
// do whatever, maybe some error?
}
For the second case, you can just say
var label = pageList[pageID] || 'notFound';
As indicated by #Pointy, either get rid of the array or subsiture pageList[0] for pageList and pageList[0][pageID] for pageList[pageID] above, if you need to keep the array.

AngularJS: 2-way binding of custom string serialization

Short Question:
How to create a <input type="text"> which contains a custom-format string serialization of an object in a way that editing the string updates the model and vice versa?
I think AngularJS’ directives are the way to go, but i can’t get it pinned down.
Long Question:
Prequel
I have a object which is my application’s “master model”. it can be serialized to a string of a specific format:
it has 2-3 attributes, whose serializations are joined by “;” (no trailing “;” if the third is missing)
attributes 2 and 3 are lists of objects, and serialized by joining those with “,”.
the serialization of the objects is just one of their string attributes, or two ow them with “x” between.
so i have a constructor (accepting a spec string), and a toString function. Following; the latter for clarity:
World.prototype.toString = function() {
var spec = [];
spec[0] = this.version;
spec[1] = this.layers.map(function(layer) {
var c = (layer.c > 1) ? layer.c + 'x' : '';
return c + layer.id; //e.g. 'T' or '2xT'
}).join(',');
//spec[2] in python: ','.join(option.id for option in options if option.checked)
var options = this.options.filter(function(option) {
return option.checked;
});
if (options.length > 0)
spec[2] = options.map(function(option) {
return option.id;
}).join(',');
return spec.join(';');
};
The directive i tried to use looks thusly, but the $watch only fires once.
angular.module('layersApp', []).directive('spec', function() {
return {
restrict: 'A',
link: function(scope, element, attrs) {
scope.$watch('world', function(val) {
element.val(val.toString());
console.log('object updated', element.val());
}, true);
element.blur(function(e) {
scope.world = new World(element.val());
});
}
};
});
Actual long question
What i want is an easy way to make this work,
<input type="text" data-ng-model="theWorld" spec>
where spec is the custom directive shown above, setting up the two-way binding
Outlook
it would be awesome if this could result in a generic “serialization” directive used like that:
<input type="text" data-serialization="string2Foo, foo2String" data-ng-model="foo">
Which would look up the object foo, and the functions string2Foo and foo2String to setup custom (de)serialization.
I think you can use of $parsers and $filters of ngModel controller.
Here is the simplest example of doing it.
http://plnkr.co/edit/13PJN2
It should be easy to add validation, too.
I tried to make it accept custom serializer from parent scope, but failed to do so. Not sure about it.

Ext.DomHelper.useDom set to true isn't working

I'm using ExtJs 3.4 and have the following code to create a hidden field:
box.hidden = this.el.insertSibling({
tag: 'input',
type: 'hidden',
value: itemVal,
name: (this.hiddenName || this.name)
}, 'before');
However, when itemVal is a json-string (or a string with quotation characters) it creates an element that looks like:
<input type="hidden" value="[" 635f7ede-7add-415f-8461-548d17027cac.group","bbe2x:101"]"="" name="selector_account_ef8e33ca71e749dca21997f51b404e23" id="ext-gen1766">
The problem is that it cocatenates the html for performance. So I want to, in this case, create the element by setting Ext.DomHelper.useDom to true. Should be an easy fix, right? But the inner code that checks the useDom variable checks against the private object that is passed to Ext.apply function instead of using Ext.DomHelper.useDom. So it doesn't matter if i set Ext.DomHelper.useDom to true inside the function that checks it, it is never true. Se the ExtJs code here:
http://docs.sencha.com/ext-js/3-4/source/DomHelper-more.html
// private
function doInsert(el, o, returnElement, pos, sibling, append){
el = Ext.getDom(el);
var newNode;
if (pub.useDom) {
...
} else {
...
}
return returnElement ? Ext.get(newNode, true) : newNode;
}
I found an old bug report for this that was closed, (http://www.sencha.com/forum/showthread.php?76966-CLOSED-3.0.0-DomHelper-s-useDom-bug) but I don't understand why and HOW I can set useDom to true.
Of course it's simple to fix it by replacing " to " but I want to understand it.

Categories

Resources