0

I am having trouble understanding the hierarchy of scopes in a nested scenario.

I am using UI router. My home template contains the;

ui-view and a directive 'gmap-locator'.   
 ui-view loads the following template/controller as default  'LocationFilter.Html/locationfilter',  within which I am nesting a directive A
        within which I am nesting a directive B
          within which I am nesting a directive C
          ... From C  I am calling a function 'funcXYZ()' on an ng-click.

I have my 'funcXYZ()' declared at home controller and 'gmap-locator' directive's controller.
I expected the 'funcXYZ()' in the home controller to be invoked but to my surprise 'funcXYZ()' in the 'gmap-locator's controller is getting called until I comment that out(then it propogates to the home controller's function).

It doesn't fall in the right hierarchy of propogation, atleast with my understanding.. any thoughts?

I am using UIRouter and my routing looks like this:

app.config(function ($stateProvider, $urlRouterProvider) {
    $urlRouterProvider.otherwise('/searchModeList');
    $stateProvider
        .state('home', {
            url: '/',
            templateUrl: '/templates/Index.html',
            controller: 'homeController'
        })
        .state('home.searchModeList', {
            url: 'searchModeList',
            templateUrl: '/templates/SearchModeList.html',
            controller: 'searchModeListController'
        })
        .state('home.countyList', {
            url: 'countyList',
            templateUrl: '/templates/countyList.html',
            controller: 'countyListController'
        })
        .state('home.locationFilters', {
            url: 'locationFilters',
            templateUrl: '/templates/locationFilters.html',
            controller: 'locationFiltersController'
        })
});

Gmap_locator directive code.. Sorry Its long/crude and has to be refactored.. but just for the reference purpose..

app.directive('gmapLocator', function ($timeout, $modal, AppModesService,$rootScope) {
    var link = function (scope, element, attrs) {
        scope.mapParameters = {
            mapContainerID: 'mappe',
            initialSettings: {
                zoom: 10,
                Lat: 39.1686269,
                Lng: -76.7757216
            },
            markerIcon: '../Content/Images/roundblue.png',
            markerSelectedIcon: '../Content/Images/orangeround.png',
            markerZoomIcon: '../Content/Images/roundgreen.png',
            crashIcon: '../Content/Images/redcrossconfirm.png'
        }
        var map, mapOptions, bounds, markers = [], previousSelectedMarker, previousCrashLocation, mapClickListener,
            prevLabel, line, mapZoomMode = 'init', lineCoordinates;
        var lat = scope.mapParameters.initialSettings.Lat;
        var lng = scope.mapParameters.initialSettings.Lng;
        mapOptions = {
            mapTypeId : google.maps.MapTypeId.ROADMAP,
            center: new google.maps.LatLng(lat, lng),
            zoom: scope.mapParameters.initialSettings.zoom
        };
        function initMap() {
            map = new google.maps.Map(document.getElementById(scope.mapParameters.mapContainerID), mapOptions);
        }
        $timeout(function () { initMap() },100);
        var mapFullZoom = function (map,position) {
            (new google.maps.MaxZoomService()).getMaxZoomAtLatLng(position, function (response) {
                var zoom = 19;
                if (response.status == google.maps.MaxZoomStatus.OK) {
                    zoom = response.zoom;
                }
                map.setZoom(zoom);
                map.setMapTypeId(google.maps.MapTypeId.HYBRID);
            });
        }
        var setMarker = function (position,title,id) {
            var marker,
                markerClickTimer, currentZoom,currentZoom;
            scope.zoomLevel = 'normal';
            scope.mapZooms = {};
                markerOptions = {
                    position: position,
                    map: map,
                    title: title,
                    id: id,
                    icon: scope.mapParameters.markerIcon
                };
                var markerClickZoomIncrement = 0;
                var referencePointsCount = scope.referencePoints.length;
                if (referencePointsCount < 15) markerClickZoomIncrement = 1;
                else if((referencePointsCount >15) && (referencePointsCount < 50)) markerClickZoomIncrement = 2;
                else if(referencePointsCount>50) markerClickZoomIncrement =4;
            marker = new google.maps.Marker(markerOptions);
            google.maps.event.addListener(marker, 'click', function () {
                var element = this;
                //scope.searchBy.keyword = null; // To reset the Reference Point filter search textbox
                //scope.pointNotFound = false; // To reset the PointNotFound button selection
                scope.referencePointChanged();// call parent scope function
                scope.referencePointSelectMode = 'map';
                AppModesService.setResult('Lat',marker.position.lat());
                AppModesService.setResult('Long', marker.position.lng());
                map.panTo(marker.position);
                if ((previousSelectedMarker) && (previousSelectedMarker.id != marker.id)) {
                    previousSelectedMarker.setIcon(scope.mapParameters.markerIcon);
                    mapZoomMode = 'init';
                }
                if ((mapZoomMode == 'init') || (previousSelectedMarker.id != marker.id)) {
                        mapZoomMode = 'selected';
                        scope.selectIntersectionId = element.id;
                        AppModesService.setSelectedReferencePoint(element.id);
                        AppModesService.setResult('ReferenceRoadName', element.title);
                        clearMapClickListener();
                        clearCrashMarker();
                        if (!scope.mapZooms.initZoomLevel) {
                            currentZoom = map.getZoom();
                            scope.mapZooms.initZoomLevel = currentZoom;
                            scope.mapZooms.initCenter = map.getCenter();
                        }
                        if (map.getMapTypeId() !== google.maps.MapTypeId.ROADMAP) {
                            map.setMapTypeId(google.maps.MapTypeId.ROADMAP);
                            map.setZoom(scope.mapZooms.initZoomLevel + markerClickZoomIncrement);
                        }
                        if (scope.zoomLevel == 'normal') {
                            map.setZoom(currentZoom + markerClickZoomIncrement);
                            scope.zoomLevel = 'lightZoom';
                        }
                        element.setIcon(scope.mapParameters.markerSelectedIcon);
                        previousSelectedMarker = element;
                }
                else if(previousSelectedMarker.id == marker.id){
                    if (mapZoomMode == 'selected') {
                        mapZoomMode = 'confirmed';
                        map.setZoom(30);
                        map.setMapTypeId(google.maps.MapTypeId.HYBRID);
                        element.setIcon(scope.mapParameters.markerZoomIcon);
                        setMapClickEvent();
                    }
                }
                $timeout(function () {
                    scope.$apply();
                });
            });
            bounds.extend(position);
            markers.push(marker);
        };
        var setMapClickEvent = function () {
            mapClickListener = google.maps.event.addListener(map, 'click', function (event) {
                clearCrashMarker();
                previousCrashLocation = new google.maps.Marker({
                    position: event.latLng,
                    map: map,
                    title: 'Crash Location',
                    icon: scope.mapParameters.crashIcon,
                    draggable: true
                });
                if ((!AppModesService.getMissingPointMode()) && (!AppModesService.getMissingPointMode())) {
                    prevLabel = document.getElementsByClassName('GLabel');
                    clearLabel();
                    label = new Label({
                        map: map
                    });
                    var offset = getOffset(event.latLng, previousSelectedMarker.position);
                    label.bindTo('position', previousCrashLocation, 'position');
                    label.set('text', offset);
                    lineCoordinates = getLineCoordinates(event);
                    clearLine();
                    drawLine(lineCoordinates);
                    AppModesService.setResult('Distance', offset);
                }
                setCrashLatLong(event.latLng);
                crashMarkerClickListener = google.maps.event.addListener(previousCrashLocation, 'click', function (event) {
                    if ((!scope.pointNotFound) && (!scope.routeNotFound)) scope.openVerifyForm();
                    else scope.openMissingForm();
                });
                google.maps.event.addListener(previousCrashLocation, 'dragstart', function () {
                    if (!scope.pointNotFound) {
                        clearLine();
                        label.set('text', '_ _ _');
                    }
                });
                google.maps.event.addListener(previousCrashLocation, 'dragend', function (event) {
                    if (!scope.pointNotFound) {
                        var offset = getOffset(event.latLng, previousSelectedMarker.position);
                        label.set('text', offset);
                        lineCoordinates = getLineCoordinates(event);
                        clearLine();
                        drawLine(lineCoordinates);
                        AppModesService.setResult('Distance', offset);
                    }
                    setCrashLatLong(event.latLng);
                });
                // Set crash location data //
            });
        }

        var clearMapClickListener = function () {
            if (mapClickListener) {
                google.maps.event.removeListener(mapClickListener);
            }
        }
        var setCrashLatLong = function (latLng) {
            AppModesService.setResult('CrashLat', latLng.lat());
            AppModesService.setResult('CrashLong', latLng.lng());
        }
        var getLineCoordinates = function (event) {
            return lineCoordinates = [
                                   event.latLng,
                                   previousSelectedMarker.position
            ];
        }
        var drawLine = function (lineCoordinates) {
            var lineSymbol = {
                path: 'M 0,-1 0,1',
                strokeOpacity: .8,
                scale: 6,
                strokeWeight: 2,
                strokeColor: 'red'
            };
            line = new google.maps.Polyline({
                path: lineCoordinates,
                strokeOpacity: 0,
                icons: [{
                    icon: lineSymbol,
                    offset: '0',
                    repeat: '20px'
                }],
                map: map
            });
        }
        var clearCrashMarker = function () {
            if (previousCrashLocation) {
                if (crashMarkerClickListener) {
                    google.maps.event.removeListener(crashMarkerClickListener);
                }
                previousCrashLocation.setMap(null);
            }
            clearLabel();
            clearLine();
        }
        var createLabel = function () {
            label = new Label({
                map: map
            });
        }
        var clearLabel = function () {
            if ((prevLabel != null) && (prevLabel.length > 0)) {
                prevLabel[0].parentNode.removeChild(prevLabel[0]);
            }
        }
        var clearLine = function () {
            if (line != null) {
                line.setMap(null);
            }
        }
        Number.prototype.toRad = function () { return this * Math.PI / 180; }
        var getOffset = function (eventPosition, markerPosition) {
            var result = -1;
            var lat1 = eventPosition.lat(),
                lon1 = eventPosition.lng(),
                lat2 = markerPosition.lat(),
                lon2 = markerPosition.lng();

            //var R = 6371; // km
            var R = 3961; // miles
                var dLat = (lat2 - lat1).toRad();
                var dLon = (lon2 - lon1).toRad();
                var lat1 = lat1.toRad();
                var lat2 = lat2.toRad();

                var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
                        Math.sin(dLon / 2) * Math.sin(dLon / 2) * Math.cos(lat1) * Math.cos(lat2);
                var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
                var d = R * c;
                if (d > 1) result = d.toFixed(2) + ' miles(s)';
                else result = (d * 1760).toFixed(2) + ' yards(s)'
            return result; // yards and miles
        }
        var getMarkerIndexById = function (id) {
            var referencePointIndex = scope.referencePoints.map(function (element) {
                return element.id
            }).indexOf(id);
            return referencePointIndex;
        }
        scope.setpointNotFound = function () {
            AppModesService.setRouteNotFound(false);
            AppModesService.setPointNotFound(true);
            resetMarkers();
            map.fitBounds(bounds);
        }
        var resetMarkers = function () {
            mapZoomMode = 'init';
            if (previousSelectedMarker) {
                previousSelectedMarker.setIcon(scope.mapParameters.markerIcon);
            }
            clearCrashMarker();
            clearLine();
            clearLabel();
            scope.selectIntersectionId = null;
            previousSelectedMarker = null;
            map.setMapTypeId(google.maps.MapTypeId.ROADMAP)
        }
        var removeMarkers = function () {
            for (var markerItem in markers) {
                markerItem.setMap(null);
            }
        }
        var resetMap = function () {
            resetMarkers();
            initMap();
        }
        scope.openVerifyForm = function (size) {
            var modalInstance = $modal.open({
                animation: true,
                templateUrl: '../templates/VerifyForm.html',
                controller: verifyFormModalController,
                size: 'lg'
            });
        }
        scope.$watch(function () { return scope.selectIntersectionId }, function () {
            if ((markers.length > 0) && (scope.referencePointSelectMode == "list") && (scope.selectIntersectionId != null)) {
                var referencePointIndex = getMarkerIndexById(scope.selectIntersectionId);
                new google.maps.event.trigger(markers[referencePointIndex], 'click');
            }
        });
        scope.$watch(function () { return scope.referencePoints; }, function (newValue, oldValue) {
            for (var count = 0; count < markers.length; count++) {
                markers[count].setMap(null);
            }
            markers = [];
            bounds = new google.maps.LatLngBounds();
            initMap();
            if(scope.referencePoints.length>0) {
                angular.forEach(scope.referencePoints, function (value, key) {
                    var position = new google.maps.LatLng(value.latitude, value.longitude);
                    setMarker(position, value.title, value.id);
                });
                map.fitBounds(bounds);
            } 
        }, true);

        //Changing watch to broadcast listener
        $rootScope.$on('pointNotFound.Changed', function (event,args) {
            if (args.state == true) setMapClickEvent();
            else clearMapClickListener();
            scope.pointNotFound = args.state;
        });
        $rootScope.$on('routeNotFound.Changed', function (event, args) {
            if (args.state == true) setMapClickEvent();
            else clearMapClickListener();
            scope.routeNotFound = args.state;
        });
        scope.$watch(function () { return scope.clearCrashMarker }, function (newValue, oldValue) {
            if (newValue == true) {
                clearCrashMarker();
            }
        });
        // receive broadcasted events
        scope.$on('clearCrashMarkerEvent', function (event, args) {
            scope.clearCrashMarker = args.state;
        });
    }
    return {
        restrict: 'EA',
        template: "<div id='mappe' style='height:693px;width:100%'></div>",//'../templates/gomap.html', 
        link: link,
        scope: false,
        replace:false
    }
});
Hasteq
  • 926
  • 13
  • 21
  • you could read this http://stackoverflow.com/questions/14049480/what-are-the-nuances-of-scope-prototypal-prototypical-inheritance-in-angularjs for understanding how nested scope works – Pankaj Parkar Aug 21 '15 at 20:03
  • 1
    Does gmap-locator have its own isolate scope? If not, it uses the home controller scope, and thus redefines the function that has already been defined by the home controller. Post your directive code. – JB Nizet Aug 21 '15 at 20:20
  • I have added the directive code. It does not have an isolated scope – Hasteq Aug 21 '15 at 21:18
  • Then you have your answer. Use an isolate scope, or at least scope: true. – JB Nizet Aug 21 '15 at 21:31
  • Could you please post this as an answer so i can mark it so. – Hasteq Aug 21 '15 at 21:48

1 Answers1

0

The gmap-locator directive doesn't have its own scope. So it actually redefines the function in the same scope as the home controller scope.

The directive should create its own scope, using scope: true, or scope: {} if you want it to be isolate.

JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255