angular-ui / ui-leaflet

AngularJS directive to embed an interact with maps managed by Leaflet library
http://angular-ui.github.io/ui-leaflet
Other
314 stars 134 forks source link

Cannot read property '_container' of null when navigating back to page with map #313

Closed ghost closed 7 years ago

ghost commented 8 years ago

I have a ui-leaflet map with a couple markers on a page in my app. When I navigate away from the page and come back, the following error is shown in the console and the markers aren't drawn.

angular.js:13920 TypeError: Cannot read property '_container' of null
    at existDomContainer (ui-leaflet.js:2032)
    at _resetUnusedMarkerGroups (ui-leaflet.js:2118)
    at _clean (ui-leaflet.js:4338)
    at _create (ui-leaflet.js:4350)
    at ui-leaflet.js:4369
    at ui-leaflet.js:2848
    at m.$digest (angular.js:17524)
    at m.$apply (angular.js:17790)
    at angular.js:19621
    at e (angular.js:5964)

Here's the line in ui-leaflet.js

var existDomContainer = function existDomContainer(groupName) {
    return angular.element(groups[groupName]._map._container).parent().length > 0;
};

My ui-leaflet directive (used within custom directive)

<leaflet class="member-density__map" markers="ctrl.mapData.markers"></leaflet>

My directive code

    return {
        restrict: 'E',
        templateUrl: 'app/portal/dashboardPanels/member-density.directive.html',
        transclude: false,
        bindToController: true,
        controllerAs: 'ctrl',
        controller: 'etMemberDensityController',
        link: function (scope, element, attrs, controller) {
            controller.initialize();
        }
    }
}])
.controller('etMemberDensityController', ['$timeout', 'leafletData', 'DashboardService', function ($timeout, leafletData, DashboardService) {
    var ctrl = this;

    // Properties
    ctrl.mapData = {
        markers: {},
        layers: {
            baselayers: {
                osm: {
                    name: 'OpenStreetMap',
                    type: 'xyz',
                    url: 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'
                }
            },
            overlays: {
                realworld: {
                    name: "MarkerData",
                    type: "markercluster",
                    visible: true
                }
            }
        }
    }

    // Helpers
    var addressPointsToMarkers = function (points) {
        return points.map(function (ap) {
            return {
                group: 'MarkerData',
                lat: ap[0],
                lng: ap[1]
            };
        });
    };

    // Initialization
    ctrl.initialize = function () {
        DashboardService.getMemberDensity().then(
            function (results) {
                var markers = addressPointsToMarkers(results);
                ctrl.mapData.markers = markers;
            });
    };
}])

Any ideas what might be causing the error? If it helps, I'm using ui-router for site routing.

ghost commented 7 years ago

Here's a Plunker that demonstrates the issue.

ghost commented 7 years ago

Nevermind, found the issue in my code.

zcsongor commented 7 years ago

I believe that is a known bug in ui-leaflet.

gecko-8 commented 7 years ago

@zcsongor Honestly I don't remember for sure but I know it had something to do with switching from group to layer in addressPointsToMakers. Here's my code as it is now:

    .directive('etMemberDensity', [function () {
        return {
            restrict: 'E',
            templateUrl: 'app/portal/dashboardPanels/memberDensity/member-density.directive.html',
            transclude: false,
            bindToController: true,
            controllerAs: 'ctrl',
            controller: 'etMemberDensityController',
            link: function (scope, element, attrs, controller) {
                controller.initialize();
            }
        }
    }])

    .controller('etMemberDensityController', ['$timeout', 'leafletData', 'DashboardService', function ($timeout, leafletData, DashboardService) {
        var ctrl = this;

        // Properties
        ctrl.mapData = {
            watchOptions: {
                markers: {
                    type: null,
                    individual: {
                        type: null
                    }
                }
            },
            events: {
                map: {
                    enable: ['moveend', 'popupopen'],
                    logic: 'emit'
                },
                marker: {
                    enable: [],
                    logic: 'emit'
                }
            },
            layers: {
                baselayers: {
                    osm: {
                        name: 'OpenStreetMap',
                        type: 'xyz',
                        url: 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
                        layerParams: {
                            showOnSelector: false
                        }
                    }
                },
                overlays: {
                    realworld: {
                        name: "Real world data",
                        type: "markercluster",
                        visible: true,
                        layerParams: {
                            showOnSelector: false
                        }
                    }
                }
            }
        }

        // Helpers
        var addressPointsToMarkers = function (points) {
            return points.map(function (ap) {
                return {
                    layer: 'realworld',
                    lat: ap[0],
                    lng: ap[1]
                };
            });
        };

        var autoSetBounds = function (map) {
            // Calculate and set the map bounds
            leafletData.getMarkers().then(
                function (markers) {
                    var bounds = null;
                    function getBoundsRecursive(obj) {
                        for (key in obj) {
                            if (obj[key].getLatLng) {
                                if (bounds) {
                                    bounds.extend(obj[key].getLatLng());
                                } else {
                                    bounds = L.latLngBounds(obj[key].getLatLng(), obj[key].getLatLng());
                                }
                            } else {
                                getBoundsRecursive(obj[key]);
                            }
                        }
                    };
                    getBoundsRecursive(markers);
                    map.fitBounds(bounds);
                });
        };

        // Initialization
        var initMap = function() {
            leafletData.getMap().then(
                function (map) {
                    map.attributionControl.setPrefix(false);
                    autoSetBounds(map);
                });
        };

        ctrl.initialize = function () {
            DashboardService.getMemberDensity().then(
                function (results) {
                    leafletData.getDirectiveControls().then(function (controls) {
                        // Process after controls is actually populated
                        $timeout(function() {
                            // Create the markers from the received data
                            var markers = addressPointsToMarkers(results);
                            controls.markers.create(markers, ctrl.mapData.markers); // Performance hit appears to be here
                            ctrl.mapData.markers = markers;

                            // Setup the map
                            initMap();
                        });
                    });
                });
        };
    }])