44

I'm AngularJS newbie. I want to create grid (using ng-grid) which height depends on window height ie. $('.gridStyle').height($(window).height() - 100);

I have written directive:

app.directive('resize', function($window) {

    return function(scope, element) {

        function applyHeight() {
            scope.height = $window.innerHeight;
            $('.gridStyle').height(scope.height - 100);
        }

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

        applyHeight();
    };
});

This works well when I resize browser window but style is not applied when site is loaded for the first time. Where can I put code to initalize height?

kolar
  • 1,010
  • 1
  • 9
  • 21

8 Answers8

82

I came across your post as I was looking for a solution to the same problem for myself. I put together the following solution using a directive based on a number of posts. You can try it here (try resizing the browser window): http://jsfiddle.net/zbjLh/2/

View:

<div ng-app="miniapp" ng-controller="AppController" ng-style="style()" resize>
    window.height: {{windowHeight}} <br />
    window.width: {{windowWidth}} <br />
</div>

Controller:

var app = angular.module('miniapp', []);

function AppController($scope) {
    /* Logic goes here */
}

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

            scope.style = function () {
                return { 
                    'height': (newValue.h - 100) + 'px',
                    'width': (newValue.w - 100) + 'px' 
                };
            };

        }, true);

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

FYI I originally had it working in a controller (http://jsfiddle.net/zbjLh/), but from subsequent reading found that this is uncool from Angular's perspective, so I have now converted it to use a directive.

Importantly, note the true flag at the end of the 'watch' function, for comparing the getWindowDimensions return object's equality (remove or change to false if not using an object).

Matty J
  • 3,116
  • 32
  • 31
  • 4
    Isn't that bad that you are `$watch`ing a function? As far as I understand, `scope.getWindowDimensions()` will be called on every `$digest` cycle, even if window was not resized. – n1313 Jul 30 '13 at 10:47
  • 1
    As I understand it, in order to watch more than one value at a time (ie. both the window's 'width' and 'height'), you need to watch a function (and thus need to have the watch function flag (last parameter) set to 'true')). If you wanted to just watch one dimension, or one value, you don't need a function. – Matty J Sep 06 '13 at 02:37
  • 2
    You could [shorten further the code into like this](http://jsfiddle.net/zbjLh/136/). – Jürgen Paul Oct 26 '13 at 03:17
  • 2
    @n1313 its perfectly normal to watch functions – psp Aug 28 '14 at 10:04
  • 1
    @MattyJ you could watch "{a:b, c: d}" expression made on the fly and theres also $watchGroup coming in angular 1.3 – psp Aug 28 '14 at 10:06
9

To avoid check on every digest cycle, we can change the height of the div when the window height gets changed.

http://jsfiddle.net/zbjLh/709/

<div ng-app="miniapp" resize>
  Testing
</div>

.

var app = angular.module('miniapp', []);

app.directive('resize', function ($window) {
    return function (scope, element) {
        var w = angular.element($window);
        var changeHeight = function() {element.css('height', (w.height() -20) + 'px' );};  
            w.bind('resize', function () {        
              changeHeight();   // when window size gets changed             
        });  
        changeHeight(); // when page loads          
    }
})
Razan Paul
  • 13,618
  • 3
  • 69
  • 61
5
angular.element(document).ready(function () {
    //your logic here
});
Engineer
  • 8,529
  • 7
  • 65
  • 105
1

A slight improvement to Bizzard's excellent answer. Supports width-offset and/or height-offset on the element, to determine how much will be subtracted from the width/height, and prevents negative dimensions.

 <div resize height-offset="260" width-offset="100">

directive:

app.directive('resize', ['$window', function ($window) {
    return function (scope, element) {
        var w = angular.element($window);
        var heightOffset = parseInt(element.attr('height-offset'));
        var widthOffset = parseInt(element.attr('width-offset'));
        var changeHeight = function () {
            if (!isNaN(heightOffset) && w.height() - heightOffset > 0)
                element.css('height', (w.height() - heightOffset) + 'px');
            if (!isNaN(widthOffset) && w.width() - widthOffset > 0)
                element.css('width', (w.width() - widthOffset) + 'px');
        };
        w.bind('resize', function () {
            changeHeight();
        });
        changeHeight();
    }
}]);

Edit This is actually a silly way of doing it in modern browsers. CSS3 has calc, which allows the calculation to be specified in CSS, like this:

#myDiv {
 width: calc(100% - 200px);
 height: calc(100% - 120px);
}

http://caniuse.com/#search=calc

Tony BenBrahim
  • 7,040
  • 2
  • 36
  • 49
0

It seems you require the following plugin: https://github.com/yearofmoo/AngularJS-Scope.onReady

It basically gives you the functionality to run your directive code after the your scope or data is loaded i.e. $scope.$whenReady

0

Combining matty-j suggestion with the snippet of the question, I ended up with this code focusing on resizing the grid after the data was loaded.

The HTML:

<div ng-grid="gridOptions" class="gridStyle"></div>

The directive:

angular.module('myApp.directives', [])
    .directive('resize', function ($window) {
        return function (scope, element) {

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

                // resize Grid to optimize height
                $('.gridStyle').height(newValue.h - 250);
            }, true);

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

The controller:

angular.module('myApp').controller('Admin/SurveyCtrl', function ($scope, $routeParams, $location, $window, $timeout, Survey) {

    // Retrieve data from the server
    $scope.surveys = Survey.query(function(data) {

        // Trigger resize event informing elements to resize according to the height of the window.
        $timeout(function () {
            angular.element($window).resize();
        }, 0)
    });

    // Configure ng-grid.
    $scope.gridOptions = {
        data: 'surveys',
        ...
    };
}
Community
  • 1
  • 1
Fabien
  • 787
  • 1
  • 6
  • 11
0

My solution if your ng-grid depend of element parent(div, layout) :

directive

myapp.directive('sizeelement', function ($window) {
return{
    scope:true,
    priority: 0,
    link: function (scope, element) {
        scope.$watch(function(){return $(element).height(); }, function(newValue, oldValue) {
            scope.height=$(element).height();
        });
    }}
})

sample html

<div class="portlet  box grey" style="height: 100%" sizeelement>
    <div class="portlet-title">
        <h4><i class="icon-list"></i>Articles</h4>
    </div>
    <div class="portlet-body" style="height:{{height-34}}px">
        <div class="gridStyle" ng-grid="gridOrderLine" ="min-height: 250px;"></div>
    </div>
</div>

height-34 : 34 is fix height of my title div, you can fix other height.

It is easy directive but it work fine.

boatcoder
  • 17,525
  • 18
  • 114
  • 178
timactive
  • 789
  • 6
  • 27
-4
$(document).ready(function() {
       scope.$apply(function() {
                applyHeight();
            });
});

This also ensures that all the scripts have been loaded before the execution of the statements. If you dont need that you can use

$(window).load(function() {
       scope.$apply(function() {
                applyHeight();
            });
});
Yash Singla
  • 144
  • 7