38

I was trying to make a div resize when the window resizes, after looking around, it seems that using a directive was the best solution.

Template:

<div elHeightResize ng-view ng-style="{ height: windowHeight }"></div>

Directive:

myApp.directive('elheightresize', ['$window', function($window) {
    return {
        link: function(scope, elem, attrs) {
            scope.onResize = function() {
                var header = document.getElementsByTagName('header')[0];
                elem.windowHeight = $window.innerHeight - header.clientHeight;
            }
            scope.onResize();

            angular.element($window).bind('resize', function() {
                scope.onResize();
            })
        }
    }
}])

While I can log elem.windowHeight in scope.onResize, it doesn't seem to apply it to ngStyle

Am I still overlooking something?

EDIT:

<div ng-view resize style="height: {{ windowHeight }}px">

This solution seems to work, still interested into why using ngStyle wasn't working.

David L
  • 32,885
  • 8
  • 62
  • 93
woutr_be
  • 9,532
  • 24
  • 79
  • 129
  • The answer to your exact question (why `elem.windowHeight` doesn't apply to `ngStyle`) is that you have to apply it manually by triggering a digest cycle, because the `.bind()` method is not intercepted by Angular. So at the end of your event handler (after `scope.onResize(); })`) you should add `scope.$apply();`. – f.ardelian Feb 04 '16 at 22:57

6 Answers6

47

I think you forgot to fire digest cycle by calling scope.$apply(); at the end of scope.onResize method

Anyways, I used following directive (took from HERE) that works for me:

Try to open debug view and change view height: Demo Fiddle

app.directive('resize', function ($window) {
    return function (scope, element, attr) {

        var w = angular.element($window);
        scope.$watch(function () {
            return {
                'h': w.height(), 
                'w': w.width()
            };
        }, function (newValue, oldValue) {
            scope.windowHeight = newValue.h;
            scope.windowWidth = newValue.w;

            scope.resizeWithOffset = function (offsetH) {

                scope.$eval(attr.notifier);

                return { 
                    'height': (newValue.h - offsetH) + 'px'
                    //,'width': (newValue.w - 100) + 'px' 
                };
            };

        }, true);

        w.bind('resize', function () {
            scope.$apply();
        });
    }
}); 

And usage:

 <div  ng-style="resizeWithOffset(165)" 
       resize 
        notifier="notifyServiceOnChage(params)"
   >
    /** ... */
 </div>

Dummy controller method usage:

$scope.notifyServiceOnChage = function(){
      console.log($scope.windowHeight);   
 };

[EDIT]

Here is demo without jQuery library by using innerHeight

Demo 3Fiddle

Community
  • 1
  • 1
Maxim Shoustin
  • 77,483
  • 27
  • 203
  • 225
  • That's rather odd, when copy / pasting your code, nothing seemed to happen for me. I then tried separating my code on its own and it works: http://jsfiddle.net/zbjLh/272/ It seems that their might be some conflicts within my app, although nothing special is happening there. – woutr_be Apr 13 '14 at 15:57
  • When I do it like this `
    ` it seems to work ...
    – woutr_be Apr 13 '14 at 16:05
  • I'm rather interested into why my code doesn't work, I could just copy / paste your code, but that doesn't solve my initial problem. – woutr_be Apr 14 '14 at 02:19
  • If you want to find the issue, try to reproduce the error in Fiddle – Maxim Shoustin Apr 14 '14 at 06:25
  • 1
    For others reading this, I think that calling `.height()` and `.width()` assume that `w` (or the results of `angular.element($window)`) is a JQuery object. In other words, this requires jQuery to be on the page. It doesn't seem to work when just using jqLite – mozz100 Sep 14 '14 at 21:45
  • @mozz100 thanks for update, I added demo without jQuery – Maxim Shoustin Sep 14 '14 at 22:02
  • $watch doesn't work as intended here, It always returns a new object, so dirty checking thinks, that it has changed and the window resized (I've tested it). If you use the same object and just change it's `x` and `y` fields, it still won't work, because angular dirty-checking is not deep, it will not notice a change of an object field. It's better to use `(w.width() * 10000) + w.height()` (or 100000 for monitors of the future :-) then each combination of width/height recieves a unique integer hash. – Yuriy Dyachkov Mar 22 '16 at 11:10
  • I seem to be getting an error with the code above. This works for me: – Rohit L Jun 10 '16 at 01:13
7

Since we work with a directive, we can always do some DOM manipulation by changing the height of the element inside the directive.

Example:

var app=angular.module('App', []);
app.directive('elheightresize', ['$window', function($window) {
    return {
        link: function(scope, elem, attrs) {
            scope.onResize = function() {
                var header = document.getElementsByTagName('header')[0];
                elem.windowHeight = $window.innerHeight - header.clientHeight;
                $(elem).height(elem.windowHeight);
            }
            scope.onResize();

            angular.element($window).bind('resize', function() {
                scope.onResize();
            })
        }
    }
}])

Live example: http://jsfiddle.net/choroshin/FJvyb/

Alex Choroshin
  • 6,177
  • 2
  • 28
  • 36
  • While this works, I'm still interested in why my code wouldn't work. – woutr_be Apr 13 '14 at 15:57
  • 3
    This may be too late to answer the question, but in case anyone lands on this, there's a bug with the above example: the movement on a resize doesn't always result in the div filling the entire area and periodically the
    element will just be the height of the text. You need to add a `scope.apply();` after the `scope.onResize();` call to ensure that the change is propagated correctly.
    – Lew Nov 26 '14 at 04:56
5

It's been a while since you asked and you seem to have gotten your workaround solution... but you also ask why ng-style didn't work:

ng-style, from the Angular docs

Expression which evals to an object whose keys are CSS style names and values are corresponding values for those CSS keys.

So in your ng-style example you are providing height : 375; (that is if windowHeight evaluated to 375) and thats not a proper value.

It should be like you have done in your workaround and have units.

windowHeight = ($window.innerHeight - header.clientHeight) + "px";

3

A minimalist one in CoffeeScript:

app.directive "applyOnResize", ($window) ->
  ($scope, $element) ->
    $element($window).bind("resize", $scope.$apply)

Same in Javascript.

Dorian
  • 22,759
  • 8
  • 120
  • 116
3

Inside link function give

scope.onResize = function() {
            var header = document.getElementsByTagName('header')[0];
            elem.windowHeight = $window.innerHeight - header.clientHeight;
        }


$window.onresize = function(){scope.onResize();scope.$apply();}
Aswin Ramesh
  • 1,654
  • 1
  • 13
  • 13
0

Here is another version without using angular $watch and just want to watch the window size change:

function resize () {

        var windowHeight = window.innerHeight,
            windowWidth  = window.innerWidth;

        scope.windowHeight = windowHeight;
        scope.windowWidth  = windowWidth;

        console.log('w.innerHeight', windowHeight);
        console.log('w.innerWidth', windowWidth);

        //** If want to apply style on element, can do something like:
        var elemStyle = {
            width: windowWidth + 'px',
            height: windowHeight + 'px'
        };

        element.css(elemStyle);
    }       
    resize();

    //** On resize
    window.onresize = function () {
        resize();
        scope.$apply();
    }

    //** Destroy
    scope.$on('$destory', function () {
        window.off('resize')
    });  

Fiddle

Neaped
  • 445
  • 5
  • 19
  • this is bad practice... global onresize handler, etc. use code from the other answers – reflog Feb 17 '15 at 15:37
  • I just give alternative suggestion to achieve the result without use $watch, this is why I write/extend the code example. I believed I did explained clearly. By the way, global onrpsize handler doesn't have problem imo, just depends on how you use it. Think about multiple dialog at one time. You may worry about the global handler will always listen on event change. But angular directive can be destroy/remove anytime. The scope that directive listen will not be a 'global' scope/module anyway. – Neaped Feb 18 '15 at 17:43