Define setter for hash member in JavaScript - javascript

I have hash defined like this:
var props = {
id: null,
title: null,
status: null
};
I'd like to define setter for status field (and only for it) doing it as following:
props.__defineSetter__("status", function(val){
//Checking correctness of val...
status = val;
});
But it doesn't work :(
So, what's the right way to do it?

Simple, you need to use
this.status = val;
Otherwise you are just setting an unrelated global variable status equal to val.
And as already noted, setters/getters are not implemented in IE.
Also, I'm not sure about how wise it is to have a setter that is the same name as the property it sets. Not sure if this will result in a conflict, but it does seem like a bad idea yes? Ideally the variable that would be set should be hidden in a closure
var props = {
id: null,
title: null
};
(function() {
var status;
props.__defineSetter__("status", function(val){
//Checking correctness of val...
status = val;
});
props.__defineGetter__('status', function() { return status; });
}());
This way, status is fully protected from direct access, which is the point of using setters and getters.

The first thing is what MooGoo has pointed out. But also, you can not assign a property setter to an object using the same name as an existing variable in the object.
So your code would have to be something arround this:
var props = {
id: null,
title: null,
hStatus: null,
};
props.__defineSetter__("status", function(v){
this.hStatus = v;
});
props.__defineGetter__("status", function(){
return this.hStatus;
});
[edit]
Yeah, MooGoo eddited his answer faster then time time I took to write this 8(.

This should work:
props.__setStatus__ = function(val) {
// Check correctness of val
this.status = val;
}
Usage:
props.__setStatus__('Alive');

Related

js get set with dynamic variables in node-opcua

Examples on node-opcua # https://github.com/node-opcua/node-opcua say that I need to rewrite code for every variable added to the OPC server, this is achieved calling 'addressSpace.addVariable()'... But if I have 1000 variables it could be an hard task... and eventually each custom user want a code rewrite, it could be tedious... so I'm trying to do it dynamically.
The opc read 'tags' from another custom server (not OPC).
With this 'tags' the opc server needs to add them to node 'device'.
When the OPC server node-opcua find a get or set of a variable coming from the net, it call the get or set of the correct variable:
for (var i = 0; i < tags.GetTags.length; i++)
{
variables[tags.GetTags[i].Tag] = {"value" : 0.0, "is_set" : false};
addressSpace.addVariable({
componentOf: device, // Parent node
browseName: tags.GetTags[i].Tag, // Variable name
dataType: "Double", // Type
value: {
get: function () {
//console.log(Object.getOwnPropertyNames(this));
return new opcua.Variant({dataType: opcua.DataType.Double, value: variables[this["browseName"]].value }); // WORKS
},
set: function (variant) {
//console.log(Object.getOwnPropertyNames(this));
variables[this["browseName"]].value = parseFloat(variant.value); // this["browseName"] = UNDEFINED!!!
variables[this["browseName"]].is_set = true;
return opcua.StatusCodes.Good;
}
}
});
console.log(tags.GetTags[i].Tag);
}
As I say I tried to use the 'this' in get and set functions with half luck, the get has a 'this.browseName' (the tag name) property that can be used to dynamic read my variables and it currently works.
The problem is with the set, in set 'this.browseName' and 'this.nodeId' don't exist! So it gives 'undefined' error. It also doesn't exist in variant variable.
Do you know a work-around to use dynamic variables with the above code? I need to have one for loop with one get and one set definitions for all variables (tags), that read and write a multi-property object or an array of objects, like 1 get and 1 set definitions that write the right variable in a n records array.
PS: I found on stack overflow this:
var foo = {
a: 5,
b: 6,
init: function() {
this.c = this.a + this.b;
return this;
}
}
But in my case node-opcua Variable doesn't has a 'this' working like the example. In the 'set' (like init): this.browseName (like a) and this.nodeId (like b) are not reachable.
Gotcha,
you need to cast get and set properties as functions like:
addressSpace.addVariable({
componentOf: device,
browseName: _vars[i].Tag,
dataType: "Double",
value: {
get: CastGetter(i),
set: CastSetter(i)
}
});
with
function CastGetter(index) {
return function() {
return new opcua.Variant({dataType: opcua.DataType.Double, value: opc_vars[index].Value });
};
}
function CastSetter(index) {
return function (variant) {
opc_vars[index].Value = parseFloat(variant.value);
opc_vars[index].IsSet = true;
return opcua.StatusCodes.Good;
};
}
you will use an index to get and set values in the array, casting function like this will provide index to be "hard coded" in those get and set properties.

Ember - Custom Computed Property to check if all dependent fields exists

I am creating a form and I am trying to find a simple, elegant way of handling to see if all inputs exist.
Form = Ember.Object.extend({
// section 1
name: null,
age: null,
isABoolean: null,
// section 2
job: null,
numberOfSiblings: null,
isComplete: Ember.computed.and('_isSection1Complete', '_isSection2Complete'),
_isSection1Complete: function() {
var isPresent = Ember.isPresent;
return isPresent(this.get('name')) && isPresent(this.get('age')) && isPresent(this.get('isABoolean'));
}.property('name', 'age', 'isABoolean'),
_isSection2Complete: function() {
var isPresent = Ember.isPresent;
return isPresent(this.get('job')) && isPresent(this.get('numberOfSiblings'));
}.property('job', 'numberOfSiblings')
});
However, this doesn't seem to scale. My actual application will have many sections (over 20 sections).
I am looking into trying to create a re-usable computed property that fits my needs. Take for example the code of what I am going for:
Form = Ember.Object.extend({
// properties...
isComplete: Ember.computed.and('_isSection1Complete', '_isSection2Complete'),
_isSection1Complete: Ember.computed.allPresent('name', 'age', 'isABoolean'),
_isSection2Complete: Ember.computed.allPresent('job', 'numberOfSiblings')
});
I feel that this is a common case, but I'm failing to find the correct computed properties on how to execute this, so I would like to make my own.
Two questions:
Where's the best place to define the custom computed property? Can I just attach a function to Ember.computed?
Is there an easier way to solve this? I feel like I'm overlooking something simple.
As for Question #1,
You can define a custom computed helper in the App namespace. In this example, I created a new computed helper called allPresent that checks each property passed in against Ember.isPresent.
App.computed = {
allPresent: function (propertyNames) {
// copy the array
var computedArgs = propertyNames.slice(0);
computedArgs.push(function () {
return propertyNames.map(function (propertyName) {
// get the value for each property name
return this.get(propertyName);
}, this).every(Ember.isPresent);
});
return Ember.computed.apply(Ember.computed, computedArgs);
}
};
It can be used like this, per your example code:
_isSection2Complete: App.computed.allPresent(['job', 'numberOfSiblings'])
I adapted this from the approach here: http://robots.thoughtbot.com/custom-ember-computed-properties
As for Question #2, I can't think of a simpler solution.
I had to make a minor adjustment to Evan's solution, but this works perfectly for anyone else that needs it:
App.computed = {
allPresent: function () {
var propertyNames = Array.prototype.slice.call(arguments, 0);
var computedArgs = propertyNames.slice(0); // copy the array
computedArgs.push(function () {
return propertyNames.map(function (propertyName) {
// get the value for each property name
return this.get(propertyName);
}, this).every(Ember.isPresent);
});
return Ember.computed.apply(Ember.computed, computedArgs);
}
};
This can now be used as such:
_isSection2Complete: App.computed.allPresent('job', 'numberOfSiblings')

Overriding a breeze entity value getter setter seems to break change tracking

I am using breeze to communicate with Web.API 2.1
In my backend I save some values as a list of strings (instead of saving one-to-many relations). In the front end I want to break these values, edit them, put them back together and persist them to the DB.
emailsString is the actual property that is persisted to the DB and exists in the model.
fullName acts as an "interface" to reading and modifying the first and last name properties.
I have the following:
function registerUserProfile(metadataStore) {
metadataStore.registerEntityTypeCtor('UserProfile', profile, profileInitializer);
function profile() {
this.fullName = '';
this.emails = [];
}
function profileInitializer(newItem) {
if (!newItem.emailsString || newItem.emailsString.length === 0) newItem.emails = [{ email: '' }];
}
Object.defineProperty(profile.prototype, 'fullName', {
get: function() {
var fn = this.firstName;
var ln = this.lastName;
return ln ? fn + ' ' + ln : fn;
},
set: function (value) {
var parts = value.split(' ');
this.firstName = parts.shift();
this.lastName = parts.shift() || '';
}
});
Object.defineProperty(profile.prototype, 'emailsString', {
get: function () {
return objectToStringArray(this.emails, 'email');
},
set: function (value) {
this.emails = stringToObjArray(value, 'email');
}
});
function objectToStringArray(objectArray, objectValueKey) {
var retVal = '';
angular.forEach(objectArray, function (obj) {
retVal += obj[objectValueKey] + ';';
});
if (retVal.length > 0)
retVal = retVal.substring(0, retVal.length - 1); //remove last ;
return retVal;
}
function stringToObjArray(stringArray, objectValueKey) {
var objArray = [];
angular.forEach(stringArray.split(';'), function (str) {
var item = {};
item[objectValueKey] = str;
objArray.push(item);
});
return objArray;
}
If I modify the emailString value and call saveChanges on breeze nothing happens. If I modify the fullName property ALL changes are detected and saveChanges sends the correct JSON object for saving (including emailString value).
From what I understand, overriding the emailString property I somehow break the change tracking for this property. fullName is not a mapped property and thus is not overriding anything so it works. Am I going the correct way? If so is there a way to notify breeze that the overriden property has changed?
In general, Breeze takes over each property on an object and insures that internally it is informed about any changes to each property. How this is done is different depending on whether you are using Angular, Knockout or Backbone ( or a custom modelLibrary adapter).
But if you plan on modifying the property yourself to do something similar you need to insure that breeze is still getting notified.
Based on your posted code I'm assuming that you are using Angular. In that case you first need to determine whether your code is getting executed before or after Breeze's code.
My guess is that if you make your changes early enough then Breeze will be able to wrap them successfully. However, if your changes occur after Breeze's then you need to insure that Breeze's code is invoked as well. Debugging into the source for this is probably your best bet. And the Breeze Angular adapter is a good source as a example of how to wrap a property that might already be wrapped with another defineProperty.

Preset Options in Object and see if it doesn't exist in allowed properties

I am trying to make a preset list of options that are allowed in my object list. Here is code
var a = function(cmd, options){
var objList = [options.search ,options.demand];
if(!(options in objList)){
console.warn('Not an Allowed * in the options Property');
}
};
or should I do
var a = function(cmd, options){
var objList = [search , demand];
if(!(options in objList)){
console.warn('Not an Allowed option in the options Property');
}
};
Basically what I want to do is set that search and demand are allowed options in the options Property so later than can do
a('cmd',{
search:'',
demand:function() {
alert('Hello');
},
//warn that the next option is not allowed
quote: function() {
alert('quote of user');
}
});
If you are having trouble understanding what I am asking please ask and I will do my best to explain a bit more.
maybe writing it like so would be better?
var a = function(cmd, options){
options = {
theme: function(color) {
$('body').css('backgroundColor',color);
},
color:''
};
};
a('cmd',{
theme:'#000'//though this is not working?
});
You could check each property in options against an array of allowed options like this:
var a = function(cmd, options){
var allowedOptions = ["search", "demand"];
var hasDisallowedOptions = false;
for (option in options) {
if(allowedOptions.indexOf(option) === -1 ) {
hasDisallowedOptions = true;
break;
}
}
// if hasDisallowedOptions is true, then there is a disallowed option
};
jsfiddle with a couple test cases/examples
A one idea of passing arguments in an object is, that it allows you to choose which argument you want to use in a function, you can simply ignore extra properties in the options object. Hence you don't need to "filter" the properties of the argument either.
Let's assume you've a function like this:
var a = function (cmd, options) {
var theme = {
backgroundColor: options.bgColor,
color: options.color
}
// Do something with theme
// Notice also, that there was no use for options.extra in this function
}
Then you invoke a like this:
a('cmd', {
bgColor: '#ff0',
color: '#000',
extra: 'This is an extra property'
});
Now you can see, extra is not used in a at all, though it was a property of the anonymous object passed to a as an argument. Also all arguments passed to a are garbage collected, unless you're not going to create a closure, i.e. returning a local value or a function from a.

Is it possible to have a change-event on a javascript-variable?

I declare a variable which gets it's value through another event out of a select-box. Since there are different events which change the variable (lets name it z), I want to get informed when the variable gets changed.
So my question is: What is the best way to get informed when a variable gets changed?
z.change(function(){}); throws an error.
Are there ways to do this without a hidden input-field or other helpers like that?
It's not really possible, but some browsers support getters and setters, and with them you could implement something that called an external function when the value is chanced. If you want this to work in all browsers, then you could go the old fashioned way of doing this:
var Item = function (val) {
this._val = val;
}
Item.Prototype.setValue = function (val) {
this._val = val;
// call external function here!
}
Item.Prototype.getValue = function () {
return this._val;
}
And then always remember to only access the property through these functions.
You should be interested in http://plugins.jquery.com/project/watch which is not as complete as SpiderMonkey's method, but actually works.
try this:
var book = {
_year: 2004,
edition: 1
};
Object.defineProperty(book, "year", {
get: function(){
return this._year;
},
set: function(newValue){
if (newValue > 2004) {
this._year = newValue;
this.edition += newValue - 2004;
}
}
});
book.year = 2005;
alert(book.edition);
I use this method wrote a bidirectional model view binding jquery plugin jQueryMV https://github.com/gonnavis/jQueryMV .

Categories

Resources