Following is a sample of my object array.
$scope.arr = [{"A":"a","B":"b"},{"A":"c","B":"d"},{"A":"e","B":"f"},{"A":"g","B":"h"}];
Now I want trigger a function whenever property "A" value changes. Basically to get the count of "A" with a not empty value. Following are the sample scenarios need to trigger.
If value of "A" changed for a newly added object to the array.
If object removed where property "A" contains a value.
If value added to empty "A" in a object
If existing value emptied of "A" in a object
I have gone through Angular watch documentation and it is a possible solution. But my problem is how to watch for the specific property("A") in any object of array.
Appreciate any help for this.
If no possible solution in Angular, is there any alternative solution with underscore.js ?
For AngularJS, there's the $scope.$watch:
$scope.$watch("arr",function (newVal,oldVal){
console.log("value has changed from "+oldVal+" to "+newVal);
},true);
But this is super expensive.
For vanilla Javascript, here's a non-standard implementation called object.watch borrowed from here
/*
* object.watch polyfill
*
* 2012-04-03
*
* By Eli Grey, http://eligrey.com
* Public Domain.
* NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
*/
// object.watch
if (!Object.prototype.watch) {
Object.defineProperty(Object.prototype, "watch", {
enumerable: false
, configurable: true
, writable: false
, value: function (prop, handler) {
var
oldval = this[prop]
, newval = oldval
, getter = function () {
return newval;
}
, setter = function (val) {
oldval = newval;
return newval = handler.call(this, prop, oldval, val);
}
;
if (delete this[prop]) { // can't watch constants
Object.defineProperty(this, prop, {
get: getter
, set: setter
, enumerable: true
, configurable: true
});
}
}
});
}
// object.unwatch
if (!Object.prototype.unwatch) {
Object.defineProperty(Object.prototype, "unwatch", {
enumerable: false
, configurable: true
, writable: false
, value: function (prop) {
var val = this[prop];
delete this[prop]; // remove accessors
this[prop] = val;
}
});
}
It can be done using $watch.
e.g.:
$scope.$watch("arr",function (newVal,oldVal){
angular.forEach(newVal,function (val,index){
if(oldVal.indexOf(val)== -1){
//your code
}
})
},true);
Here third argument 'true' is for deep checking.
Related
I'm wondering when I should use
Object.defineProperty
to create new properties for an object. I'm aware that I'm able to set things like
enumerable: false
but when do you need this really? If you just set a property like
myObject.myprop = 5;
its descriptors are all set to true, right? I'm actually more curious when you guys use that rather verbose call to .defineProperty() and for what reasons.
Object.defineProperty is mainly used to set properties with specific property descriptors (e.g. read-only (constants), enumerability (to not show a property in a for (.. in ..) loop, getters, setters).
"use strict";
var myObj = {}; // Create object
// Set property (+descriptor)
Object.defineProperty(myObj, 'myprop', {
value: 5,
writable: false
});
console.log(myObj.myprop);// 5
myObj.myprop = 1; // In strict mode: TypeError: myObj.myprop is read-only
Example
This method extends the Object prototype with a property. Only the getter is defined, and the enumerability is set to false.
Object.defineProperty(Object.prototype, '__CLASS__', {
get: function() {
return Object.prototype.toString.call(this);
},
enumerable: false // = Default
});
Object.keys({}); // []
console.log([].__CLASS__); // "[object Array]"
Features like 'enumerable' are rarely used in my experience.
The major use case is computed properties:
var myObj = {};
myObj.width = 20;
myObj.height = 20;
Object.defineProperty(myObj, 'area', {
get: function() {
return this.width*this.height;
}
});
console.log(myObj.area);
A really good reason for using Object.defineProperty is that it lets you loop through a function in an object as a computed property, which executes the function instead of returning the function's body.
For example:
var myObj = {};
myObj.width = 20;
myObj.height = 20;
Object.defineProperty(myObj, 'area', {
get: function() {
return this.width*this.height;
},
enumerable: true
});
for (var key in myObj) {
if (myObj.hasOwnProperty(key)) {
console.log(key + " -> " + myObj[key]);
}
}
//width -> 20, height -> 20, area -> 400
Versus adding the function as a property to an object literal:
var myObj = {};
myObj.width = 20;
myObj.height = 20;
myObj.area = function() {
return this.width*this.height;
};
for (var key in myObj) {
if (myObj.hasOwnProperty(key)) {
console.log(key + " -> " + myObj[key]);
}
}
// width -> 20, height -> 20, area -> function() { return this.width*this.height;}
Make sure you set the enumerable property to true in order to loop through it.
For example, that's how Vue.js keeps track of changes in the data object:
When you pass a plain JavaScript object to a Vue instance as its data option, Vue will walk through all of its properties and convert them to getter/setters using Object.defineProperty. This is an ES5-only and un-shimmable feature, which is why Vue doesn’t support IE8 and below.
The getter/setters are invisible to the user, but under the hood they enable Vue to perform dependency-tracking and change-notification when properties are accessed or modified.
[...]
Keep in mind that even a super slim and basic version of Vue.js would use something more than just Object.defineProperty, but the main functionality comes from it:
Here you can see an article where the author implements a minimal PoC version of something like Vue.js: https://medium.com/js-dojo/understand-vue-reactivity-implementation-step-by-step-599c3d51cd6c
And here a talk (in Spanish) where the speaker builds something similar while explaining reactivity in Vue.js: https://www.youtube.com/watch?v=axXwWU-L7RM
Summary:
In Javascript Objects are collections of key-value pairs.
Object.defineProperty() is a function which can define a new property on an object and can set the following attributes of a property:
value <any>: The value associated with the key
writable <boolean>: if writable is set to true The property can be updated by assigning a new value to it. If set to false you can't change the value.
enumerable <boolean>: if enumerable is set to true Property can be accessed via a for..in loop. Furthermore are the only the enumerable property keys returned with Object.keys()
configurable <boolean>: If configurable is set to false you cannot change change the property attributes (value/writable/enumerable/configurable), also since you cannot change the value you cannot delete it using the delete operator.
Example:
let obj = {};
Object.defineProperty(obj, 'prop1', {
value: 1,
writable: false,
enumerable: false,
configurable: false
}); // create a new property (key=prop1, value=1)
Object.defineProperty(obj, 'prop2', {
value: 2,
writable: true,
enumerable: true,
configurable: true
}); // create a new property (key=prop2, value=2)
console.log(obj.prop1, obj.prop2); // both props exists
for(const props in obj) {
console.log(props);
// only logs prop2 because writable is true in prop2 and false in prop1
}
obj.prop1 = 100;
obj.prop2 = 100;
console.log(obj.prop1, obj.prop2);
// only prop2 is changed because prop2 is writable, prop1 is not
delete obj.prop1;
delete obj.prop2;
console.log(obj.prop1, obj.prop2);
// only prop2 is deleted because prop2 is configurable and prop1 is not
Object.defineProperty prevents you from accidentally assigning values to some key in its prototype chain. With this method you assign only to that particular object level(not to any key in prototype chain).
For example:
There is an object like {key1: value1, key2: value2} and you don't know exactly its prototype chain or by mistake you miss it and there is some property 'color' somewhere in prototype chain then-
using dot(.) assignment-
this operation will assign value to key 'color' in prototype chain(if key exist somewhere) and you will find the object with no change as .
obj.color= 'blue'; // obj remain same as {key1: value1, key2: value2}
using Object.defineProperty method-
Object.defineProperty(obj, 'color', {
value: 'blue'
});
// now obj looks like {key1: value1, key2: value2, color: 'blue'}. it adds property to the same level.Then you can iterate safely with method Object.hasOwnProperty().
One neat use case I have seen for defineProperty is for libraries to provide an error property to the user which, if it's not accessed within a certain interval you would log the error yourself. For example:
let logErrorTimeoutId = setTimeout(() => {
if (error) {
console.error('Unhandled (in <your library>)', error.stack || error);
}
}, 10);
Object.defineProperty(data, 'error', {
configurable: true,
enumerable: true,
get: () => {
clearTimeout(logErrorTimeoutId);
return error;
},
});
Source for this code: https://github.com/apollographql/react-apollo/blob/ddd3d8faabf135dca691d20ce8ab0bc24ccc414e/src/graphql.tsx#L510
A good use is when you need to do some interception or apply a classical Observer/Observable pattern in a elegant way:
https://www.monterail.com/blog/2016/how-to-build-a-reactive-engine-in-javascript-part-1-observable-objects
A very useful case is to monitor changes to something and act on them. It's easy because you can have callback functions fire whenever the value gets set. Here's a basic example.
You have an object Player that can be playing or not playing. You want something to happen right when it starts playing, and right when it stops playing.
function Player(){}
Object.defineProperty(Player.prototype, 'is_playing', {
get(){
return this.stored_is_playing; // note: this.is_playing would result in an endless loop
},
set(newVal){
this.stored_is_playing = newVal;
if (newVal === true) {
showPauseButton();
} else {
showPlayButton();
}
}
});
const cdplayer = new Player();
cdplayer.is_playing = true; // showPauseButton fires
This answer is related to a couple other answers here, which are good stepping points for more information, but with no need to follow external links to read about libraries or programming paradigms.
#Gerard Simpson
If 'area' should be enumerable it can be written without Object.defineProperty, too.
var myObj = {
get area() { return this.width * this.height }
};
myObj.width = 20;
myObj.height = 20;
for (var key in myObj) {
if (myObj.hasOwnProperty(key)) {
console.log(key + " -> " + myObj[key]);
}
}
//area -> 400, width -> 20, height -> 20
I want my object to have a field that when read returns the fields value, and when a val is written to the field, I want to modify the val before writing it. My current code is this:
function Cat(lives) {
var self = this;
var privateLives;
Object.defineProperty(self, 'publicLives', {
get: function() {return privateLives;},
set: function(val) {privateLives = 7 * val;}
});
}
Is there a way to do this without making a private variable? Ideally I would simply have the setter be this:
function(val) {self.publicLives = 7 * val;}
but that causes an overflow as the setter calls itself. Is there some way to make so it just doesn't loop the setter (so only assignment outside the setter's scope calls the setter and assignment in the setter just does normal assignment)? If that's possible, I wouldn't need to explicitly define a getter either as the setter writes to a public field.
No, this is not possible - a property can only be either a data property or an accessor property, not both. Of course you don't necessarily need to store the value in a private variable from your setter, you can use a different property or a property on a different object (like in #Oriol's proxy) as well. If you want to avoid private variables, "private" properties are the standard approach:
function Cat(lives) {
this.publicLives = lives;
}
Object.defineProperty(Cat.prototype, 'publicLives', {
get: function() {return this._privateLives;},
set: function(val) { this._privateLives = 7 * val;}
});
But you can also do some tricky things and hide the "private variable" by using a constant getter function that is repeatedly redefined:
Object.defineProperty(Cat.prototype, 'publicLives', {
set: function setter(val) {
val *= 7;
Object.defineProperty(this, 'publicLives', {
get: function() { return val; }
set: setter,
configurable: true
});
}
});
In ES6, an alternative would be using a Proxy object with a [[Set]] trap:
function Cat(lives) {
return new Proxy(this, {
set: function(target, prop, val) {
target[prop] = val;
if (prop === 'publicLives') target[prop] *= 7;
return true;
}
});
}
/**
* #author paula
*/
var myObj = {};
Object.defineProperty(myObj, "prop1", {
get: function(){return this._prop1;},
set:function(value){this._prop1 = value;},
enumerable:true});
Object.keys(myObj) // Note that returns ["prop1"]
myObj.prop1 = "Hello"; // call set
Object.keys(myObj); // Returns ["prop1", "_prop1"]
myObj // Object {prop1: "Hello", _prop1: "Hello"}
I have as a prerequisite an empty object to be populated with properties using Object. defineProperty. The dilemma is that I wanted to create only one property - ["prop1"] and in the example above, based on what Object.keys() returns, it looks like that 2 properties were created - ["prop1", "_prop1"].
Question: What is the name for "prop1" - is a property or it's kind of pseudo-property ?
Is it correct to be used the name property for both "prop1" and "_prop1" ?
I also tried this solution:
var myObj1 = {};
Object.defineProperty(myObj1, "prop1", {
get: function(){return this.prop1;},
set:function(value){this.prop1 = value;},
enumerable:true});
myObj1.prop1= "Bye"
and got this error: "RangeError: Maximum call stack size exceeded" which is triggered because set calls the same code over and over again in an infinite loop. I was wondering if there is any solution to this "RangeError: Maximum call stack ..." problem ? (possible duplicate). Thanks.
Question: What is the name for "prop1" - is a property or it's kind of pseudo-property ? Is it correct to be used the name property for both "prop1" and "_prop1" ?
Yes, both are properties. prop1 is an accessor property (with getters/setters) while _prop1 is a data property (simple, writable value).
To solve your problem, just don't use an accessor property:
Object.defineProperty(myObj, "prop1", {
// value: undefined,
writable: true,
enumerable: true
});
If you need an accessor property for some reason, store the value either in a closure variable or in a non-enumerable "hidden" property:
(function() {
var value;
Object.defineProperty(myObj, "prop1", {
get: function(){ return value; },
set: function(v){ value = v; },
enumerable:true
});
})();
Object.defineProperties(myObj, {
"_prop1": {
enumerable: false,
writable: true
},
"prop1": {
get: function(){ return this._prop1; },
set: function(value){ this._prop1 = value; },
enumerable:true
}
});
Object.keys(myObj) // ["prop1"]
When you do this in your setter:
this._prop1 = value;
you're creating the "_prop1" property. If you don't want it to be enumerable, define it that way:
Object.defineProperty(myObj, "_prop1", { enumerable:false });
I have an object like this:
var obj = {key1: "hello",
key2: {keyA: "1", keyB: "2"}}
I am currently using this code to watch when a values of a selected key changes:
if (!Object.prototype.watch) {
Object.defineProperty(Object.prototype, "watch", {
enumerable : false,
configurable : true,
writable : false,
value : function(prop, handler) {
var oldval = this[prop], newval = oldval, getter = function() {
return newval;
}, setter = function(val) {
oldval = newval;
return newval = handler.call(this, prop, oldval, val);
};
if (
delete this[prop]) {// can't watch constants
Object.defineProperty(this, prop, {
get : getter,
set : setter,
enumerable : true,
configurable : true
});
}
}
});
}
// object.unwatch
if (!Object.prototype.unwatch) {
Object.defineProperty(Object.prototype, "unwatch", {
enumerable : false,
configurable : true,
writable : false,
value : function(prop) {
var val = this[prop];
delete this[prop];
// remove accessors
this[prop] = val;
}
});
}
obj.watch("key1", function(id, oldval, newval) {
console.log(newval)
});
`
and when "hello" changes i can handle events. But how could i handle an event when the property of an object changes (Key2) without watchin' with the previous function all the elements of the object(KeyA and KeyB)?
better said.. When an object changes is possible to get notified without monitoring all elements of that object?
Thaaanku
When an object [property] changes is possible to get notified without monitoring all elements of that object?
No. Your watch function relies on getters/setters, which are always property-specific. What you can do:
Implement your object as a Proxy, which is designed to do exactly what you want. Yet, it is only a draft and currently only supported in Firefox' Javascript 1.8.5.
Use Object.observe, which however is only a draft as well.
Use an explicit getter/setter method that gets the updated property name as an argument
As you can see with the polyfill that enables watch to work on non-compliant Browers, watching for property change can be easily implemented in javascript.
Since what you need is different from any existing feature, you have to go for a custom feature, that will look for any change down the property chain, with a property change handler like : function handleObjectChange(obj, oldPropValue, newPropValue, propertyPath) where propertyPath is an array leading to the property that changed (expl1 : ['KeyA'] ; expl2 : ['TimeData', 'duration'] ), and oldPropValue/ newPropValue are the old/new value for that property that just changed.
propertyPath is null/undefined when the object itself changed, and in this case old/new PropertyValue contains the old/new value of the object.
Obviously that's just one way to achieve what you seek, but the basic point is : since it is a specific need, build your own specific answer.
In Firefox, I've got several objects that I need to trigger an event when a particular property of each is changed. I'm using object.watch(), however when I return the value of the property that was changed using "this", it returns the old value the first time, and "undefined" the second and subsequent times:
var myObject = {
"aProperty": 1
};
function propChanged(prop) {
alert(prop);
}
myObject.watch("aProperty", function () {
propChanged(this.aProperty);
});
myObject.aProperty = 2;//alerts "1"
myObject.aProperty = 3;//alerts "undefined"
The reason I can't just say alert(myObject.aProperty) is because this is meant to be a dynamic code that will apply the event handler to several, possibly unknown objects.
I'm just unsure exactly how to dynamically get the new value of the property using the watch method. I'm setting up a prototype for IE for this, so I'm not worried about it not working there. I just need to understand "this" and how it applies to the watch method's owner.
Edit>>
Here's the new code I'm using for cross browser, including the IE et al prototype:
var myObject = {};
if (!Object.prototype.watch) {
Object.prototype.watch = function (prop, handler) {
var oldval = this[prop], newval = oldval,
getter = function () {
return newval;
},
setter = function (val) {
oldval = newval;
return newval = handler.call(this, prop, oldval, val);
};
if (delete this[prop]) { // can't watch constants
if (Object.defineProperty) // ECMAScript 5
Object.defineProperty(this, prop, {
get: getter,
set: setter
});
else if (Object.prototype.__defineGetter__ && Object.prototype.__defineSetter__) { // legacy
Object.prototype.__defineGetter__.call(this, prop, getter);
Object.prototype.__defineSetter__.call(this, prop, setter);
}
}
};
}
if (!Object.prototype.unwatch) {
Object.prototype.unwatch = function (prop) {
var val = this[prop];
delete this[prop]; // remove accessors
this[prop] = val;
};
}
function propChanged(t, p, o, n) {
alert(o);
}
Object.defineProperty(myObject, "aProperty", {value: 2,
writable: true,
enumerable: true,
configurable: true});
myObject.watch("aProperty", propChanged);
myObject.aProperty = 3; //alerts 3
myObject.aProperty = 4; //alerts 4 (n is undefined in propChanged?
You need to return the value you want the property to have from the function you pass to watch.
myObject.watch("aProperty", function (prop, oldval, newval) {
propChanged(newVal);
return newVal;
});
should do it.
See the MDN docs for a full detail of the function but the relevant bit is
Watches for assignment to a property named prop in this object, calling handler(prop, oldval, newval) whenever prop is set and storing the return value in that property. A watchpoint can filter (or nullify) the value assignment, by returning a modified newval (or by returning oldval).
EDIT
Your edited code might work better this way
Object.prototype.watch = function (prop, handler) {
var fromPrototype = !Object.hasOwnProperty.call(this, prop),
val = this[prop],
getter = function () {
return fromPrototype ? Object.getPrototypeOf(this)[prop] : val;
},
setter = function (newval) {
fromPrototype = false;
return val = handler.call(this, prop, val, newval);
};
if (delete this[prop]) { // can't watch constants
if (Object.defineProperty) { // ECMAScript 5
Object.defineProperty(this, prop, {
get: getter,
set: setter,
configurable: true,
enumerable: true
});
} else if (Object.prototype.__defineGetter__ && Object.prototype.__defineSetter__) { // legacy
Object.prototype.__defineGetter__.call(this, prop, getter);
Object.prototype.__defineSetter__.call(this, prop, setter);
}
}
};