19

I'm using Angular & Bootstrap, with the nav tab control to switch visibility of divs. In one div, I have a large img (CAD drawing of building). I also then overlay markers on the image. I want to scale the x/y position of the markers based on the image width & image naturalWidth. I'm using a resize directive to detect changes and update my scope.

My problem is that if user switches tabs and switches back to the div with the CAD img, the refresh doesn't happen until I resize the browser (or surprisingly if I press the CMD key on Mac).

Is there an angular way to trigger the resize event to force my markers to be recalculated. Or is there an event I can tap into that is fired when the is fully displayed ?

Or is there a more refined angular approach I should take?

This is the HTML, the resize directive I've written is on the tag.

<div id="imageDiv" style="position: relative;"  ng-show="selectedLocation"  >
  <img ng-src="../maps/image/{{selectedLocation}}" 
       style=" max-width: 100%; max-height: auto; border:solid 1px black" resize  imageonload />
</div>

And this is the resize directive (adapted from http://jsfiddle.net/jaredwilli/SfJ8c/)

directive('resize', function($window) {
  return function(scope, element) {
    var w = angular.element($window);

    scope.imgCadImage = element;

    scope.getWindowDimensions = function() {
      return {
        'h' : scope.imgCadImage[0].width,
        'w' : scope.imgCadImage[0].height
      };
    };
    scope.$watch(scope.getWindowDimensions, function(newValue, oldValue) {
      if (scope.imgCadImage[0].naturalWidth)
        scope.imgScale = newValue.w / scope.imgCadImage[0].naturalWidth;
      else
        scope.imgScale = 1;
      console.log("watched resize event - scale = "+scope.imgScale)
      scope.updateCADMarkersAfterResize();
    }, true);
    w.bind('resize', function() {
      console.log("'resize' - scale = "+scope.imgScale)
      scope.$apply();
    });
  };
}).
Kevin
  • 11,521
  • 22
  • 81
  • 103
  • Have you tried adding a attribute based behavioral directive to the div containing the image which calls the resize in it's `link` function? – Marc Kline May 13 '14 at 17:38
  • @MarcKline - what's the div link function? Can you point me at some more info about it. Google search isn't too helpful. – Kevin May 13 '14 at 18:39
  • Let me actually first ask you this: is your resize directive a) attached to the image or its container and b) is it bound/triggered only by the `window.resize` event? It would be most helpful if you could provide some pseudocode demonstrating what you've described, but if you can answer those questions I might be able to take a guess at a solution. – Marc Kline May 13 '14 at 19:21
  • Code attached - I confess I don't have the fullest understanding of the directive I adopted for my use. I tried adding the resize directive to the containing div, but it didn't anything. – Kevin May 13 '14 at 20:18

3 Answers3

22

This worked for me when the above did not.

$timeout(function() {
    $window.dispatchEvent(new Event("resize"));
}, 100);

I had to also use $timeout with a delay in my case to prevent errors about digest cycles already being in progress. Not all uses cases may need this.

dispatchEvent is well supported http://caniuse.com/#feat=dispatchevent

However, it you need IE9 and IE10 support you'll have to use their propritary method: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/fireEvent

Chris Barr
  • 29,851
  • 23
  • 95
  • 135
  • 1
    using a >0 timeout delay in this scenario is almost certainly a bad idea. >0 timeouts shouldn't be used to solve timing problems, they should only be used when you actually want something to happen in that much time (e.g. an animation). You have absolutely no guarantee that 100ms is the appropriate amount of time to avoid a timing issue on any device. – tomfumb Mar 28 '17 at 18:31
  • 1
    Worked for me without the delay, putting the timeout there just forces it onto the next digest cycle – Ben Schenker Jun 19 '17 at 18:24
12

Try injecting $timeout into the resize directive:

directive('resize', function($window, $timeout) {

... then add the following line to the bottom of it:

$timeout(function(){ w.triggerHandler('resize') });

This should trigger the handler you have bound to $window.resize just after the browser renders.

Marc Kline
  • 9,399
  • 1
  • 33
  • 36
  • 1
    This did the trick, thanks. I also had to add the call to $timeout(function(){ w.triggerHandler('resize') }); in areas where I change tab control too. – Kevin May 15 '14 at 14:49
  • 1
    slight gotcha: `triggerHandler` only triggers events that are added by jqLite/jQuery. If your event is added with pure javascript (ie. `addEventListener`), you're SOL. See [this question](http://stackoverflow.com/questions/34804800/angularjs-doesnt-trigger-event-handled-by-addeventlistener) and [this question](http://stackoverflow.com/questions/2490825/how-to-trigger-event-in-javascript) for more info. – adamdport Aug 11 '16 at 17:58
1

The accepted answer did not work for me - w is undefined.

This did:

$timeout(function(){
    $(window).trigger('resize');
});
Devin McQueeney
  • 1,277
  • 2
  • 14
  • 31