0

I use AngularJS, and i have a factory, called storage to share some data between controllers and directives.

ell.on('contextmenu', function(event){
    scope.$apply(function(){
        event.preventDefault();
        storage.contextmenu.isOpen = true;
        storage.contextmenu.top     = 100;
        storage.contextmenu.left    = 200;
    });
});

The problem is, at the first time the contextmenu is undefined. But i don't want to set here, because the contextmenu may have additional properties and they have reference.

Yes, i know, i can solve this problem if i use if statement like this:

ell.on('contextmenu', function(event){
    scope.$apply(function(){
        event.preventDefault();

        if(typeof storage.contextmenu == 'undefined'){
            storage.contextmenu = {}
        }

        storage.contextmenu.isOpen = true;
        storage.contextmenu.top     = 100;
        storage.contextmenu.left    = 200;
    });
});

But i want to know is there a getter overload solution to do this as default. So when i set the value of isOpen at the first time and the contextmenu is undefined, instead of the js throw the error, it set the contextmenu to an object and set isOpen to true?

In php __get() function do the magic, is there someting same in js?

Ahmad Baktash Hayeri
  • 5,802
  • 4
  • 30
  • 43
Twois
  • 548
  • 1
  • 7
  • 18
  • Possible duplicate of [JavaScript getter for all properties](http://stackoverflow.com/questions/994143/javascript-getter-for-all-properties) – Annihlator Jun 15 '16 at 14:23
  • I think your solution of creating the object if not defined is the way to go, but there isn't a magic function like __get – SoluableNonagon Jun 15 '16 at 14:31
  • ` if( !storage.contextmenu ) ` is probably a little easier than doing typeof and would also evaluate to true for any other false-y value like null or '' – SoluableNonagon Jun 15 '16 at 14:32
  • actually you can leave existing contextmenu object if it's defined, do this storage.contextmenu = storage.contextmenu || {} – ForceUser Jun 15 '16 at 16:19

1 Answers1

0

You can use angular $parse

$parse('contextmenu.isOpen').assign(storage, true);
$parse('contextmenu.top').assign(storage, 100);

Or if you are preverted enough i know at least two ways for you

1) do it with Proxy (sacrifice performance and browser support):

here test example

function pathBuilder(src, path) {
    path = path || [];
    src = src || {};
    return new Proxy(src, {
        get: function (target, name) {
            function isenum(name) {// is property enumerable, including inherited props
                for (var n in src) {
                    if (n === name) return true;
                }
                return false;
            }

            var val;
            if (isenum(name)) {
                val = ({}).valueOf.call(src[name]); // converting primitive type to object
            }
            else if (!(name in src)) {
                val = {};
            }
            else {
                return src[name];
            }
            path.push({
                obj: src,
                prop: name
            });
            return pathBuilder(val, path);
        },
        set: function (target, name, val) {
            src[name] = val;
            var p, current = src;
            while (path.length) {
                p = path.pop();
                p.obj[p.prop] = current;
                current = p.obj;
            }
        }
    });
}


var o1 = {};
var o2 = {someprop: 'sdasdasd'};
var p1 = pathBuilder(o1);
var p2 = pathBuilder(o2);

p1.a.b.c = 12;
console.log(o1);
console.log(JSON.stringify(o1)); // {"a":{"b":{"c":12}}}

p2.someprop.foo.bar = 'value';
console.log(o2); // someprop converts from primitive string to String object
                 // to perform assignment of foo property
console.log(p2.someprop.foo.bar);// "value"
console.log(JSON.stringify(o2)); // {"someprop":"sdasdasd"}

And i made repo https://github.com/forceuser/access-deep

2) Make custom code tranfromer (for example Babel plugin) that will replace all your code with conditional assignments (no support for dynamic properties)

for example:

obj.foo.bar = 1;

transform to:

(obj.foo = obj.foo || {})&&(obj.foo.bar = 1);
ForceUser
  • 1,129
  • 1
  • 15
  • 23