allenhwkim / angularjs-google-maps

The Simplest AngularJS Google Maps V3 Directive
http://ngmap.github.io
MIT License
1.52k stars 516 forks source link

SetStyle not binding properly (view model / vm issues) #874

Open capesean opened 6 years ago

capesean commented 6 years ago

On these lines: https://github.com/allenhwkim/angularjs-google-maps/blob/master/build/scripts/ng-map.js#L1488-L1492

The value of val is vm.setStyle, which should be the function on the view model, but it is being set as a string, 'vm.setStyle'. So the function doesn't ever get called.

Version

1.18.4

Test Case

https://plnkr.co/edit/P7yv0gkrbTFeYWbRbWs8?p=preview

capesean commented 6 years ago

I've worked out why. If you're using an attribute which has the dot-notation, e.g. using a view model object like vm.yourFunction, then line 1488 will evaluate as false, as it's effectively evaluating it as a string with a dot in it, i.e. scope["vm.yourFunction"] which will be undefined.

I've replaced lines lines 1477-1500 with the following:

                link: function (scope, element, attrs, mapController) {
                    mapController = mapController[0] || mapController[1];
                    var filtered = parser.filter(attrs);
                    var options = parser.getOptions(filtered, { scope: scope });
                    var events = parser.getEvents(scope, filtered, events);

                    void 0;
                    var fn = undefined;
                    NgMap.getMap(mapController.map.id).then(function (map) {
                        //options
                        for (var key in options) {
                            var val = options[key];
                            if (typeof (fn = (function (o, s) {
                                s = s.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
                                s = s.replace(/^\./, '');           // strip a leading dot
                                var a = s.split('.');
                                for (var i = 0, n = a.length; i < n; ++i) {
                                    var k = a[i];
                                    if (k in o) {
                                        o = o[k];
                                    } else {
                                        return;
                                    }
                                }
                                return o;
                            })(scope, val)) === "function") {
                                map.data[key](fn);
                            } else {
                                map.data[key](val);
                            }
                        }

                        //events
                        for (var eventName in events) {
                            map.data.addListener(eventName, events[eventName]);
                        }
                    });
                }

The inline function function (o, s) will navigate down through the dot-notation / nested objects to get the function/property required.

There's definitely a more elegant way to incorporate this, but here it is for anyone else looking for a quick fix.