When has ng-switch finished rendering?

有些话、适合烂在心里 提交于 2020-01-06 03:17:06

问题


I'm using ui-router and trying to instantiate a widget that takes as a parameter a DOM element specified by id. This DOM element is in a <div ng-switch> and I want to call the widget constructor when the element is guaranteed to exist.

<div ng-switch on="state">
  <div ng-switch-when="map">
    <div id="map"></div>
  </div>
</div>

From the ui-router lifecycle, I understand that I should hook into $viewContentLoaded. This, however, doesn't work - the DOM element within the ng-switch isn't created at that point:

app.config(['$stateProvider', function ($stateProvider) {
  $stateProvider
    .state('/', {url: '/', templateUrl: 'index.html'})
    .state('map', {url: 'map', templateUrl: 'map.html', controller: 'MapCtrl'})
}]);

app.controller('MapCtrl', ['$rootScope', '$scope', '$state', function MapCtrl($rootScope, $scope, $state) {
  $scope.state = $state.current.name;  // expose the state to the template so we can ng-switch. Apparently there's no better way: https://github.com/angular-ui/ui-router/issues/1482

  $scope.$on('$viewContentLoaded', function mapContentLoaded(event, viewConfig) {
    var mapElement = document.getElementById('map');
    console.log('Attempting to create map into', mapElement);
    var map = new google.maps.Map(mapElement);  // <-- but mapElement will be null!
  });
}]);

What does work is using a setTimeout() of 50ms in the controller, which is brittle, but by that time the DOM element is created. Alternatively, I can set an interval, check for the presence of the map DOM element, and clear the interval when it's found.

What is the proper way of figuring out when an ng-switch has rendered its DOM? This isn't documented.

Here's the Plunkr.


回答1:


I think you're falling in the trap that many experienced front-end developers fall in to when using Angular. In most of other JS libraries we modify the DOM after it's been created and then add functionality to it. However, in Angular the functionality is defined in the HTML. Functionality and interactivity is created by using directives.

In jQuery something like this is fine:

<div id="foobar">
    Click here to do stuff
</div>

<script type="text/javascript">
    $(function () {
        $('#foobar').on('click', function () {
            someService.doStuff();
        });
    });
</script>

Whereas in Angular something like the below is more idiomatic:

<div id="foobar" ng-controller="Main" ng-click="doStuff()">
    Click here to do stuff
</div>

<script type="text/javascript">
    app.controller('Main', ['$scope', 'somerService', function ($scope, someService) {
        $scope.doStuff = function () {
            someService.doStuff();
        }
    }]);
</script>

As for your GoogleMap directive this is by far the simplest way to accomplish it. Albeit this is incredibly basic and may not do everything you need it to.

app.directive('googleMap', [function() {
    return {
      link: function(element) {
        new google.maps.Map(element);
      }
    }
  }
]);

Your map.html:

<div ng-switch on="state">
  <div ng-switch-when="map">
    <div google-map id="map"></div>
  </div>
</div>

As you mentioned however, this would recreate the Google map every time that controller is hit. One way around that is to save off the element and Map api and replacing it on subsequent calls:

app.directive('googleMap', [function () {
    var googleMapElement,
            googleMapAPI;
    return {
        link: function (element) {
            if (!googleMapElement || !googleMapAPI) {
                googleMapAPI = new google.maps.Map(element);
                googleMapElement = element;
            }
            else {
                element.replaceWith(googleMapElement);
            }

        }
    }
}]);


来源:https://stackoverflow.com/questions/26579678/when-has-ng-switch-finished-rendering

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!