Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS: Better way to watch for height change

I'm having the old variable height navigation problem: A position: fixes navigation on top and a content with margin-top: $naviHeight below. The navigation can change height when data is loaded asynchronously and so the content's margin has to change with it.

I want this to be self contained. So no code where the data is loaded but only in the involved html-elements/directives.

Currently I'm doing it in AngularJS 1.2.0 with a timer like this:

/*
* Get notified when height changes and change margin-top
 */
.directive( 'emHeightTarget', function(){
    return {
        link: function( scope, elem, attrs ){

            scope.$on( 'heightchange', function( ev, newHeight ){

                elem.attr( 'style', 'margin-top: ' + (58+newHeight) + 'px' );
            } );
        }
    }
})

/*
* Checks this element periodically for height changes
 */
.directive( 'emHeightSource', ['$timeout', function( $timeout ) {

    return {
        link: function( scope, elem, attrs ){

            function __check(){

                var h = elem.height();

                if( h != scope.__height ){

                    scope.__height = h;
                    scope.$emit( 'heightchange', h );
                }
                $timeout( __check, 1000 );
            }
            __check();
        }
    }

} ] )

This has the obvious drawback of using the timer (which i feel kind of ugly) and a certain delay after the navigation resized until the content is moved.

Is there a better way to do this?

like image 866
Scheintod Avatar asked Sep 27 '13 10:09

Scheintod


4 Answers

This works by registering a watcher in emHeightSource which is called every $digest. It updates the __height property which is in turn watched in emHeightTarget:

/*
 * Get notified when height changes and change margin-top
 */
.directive( 'emHeightTarget', function() {
    return {
        link: function( scope, elem, attrs ) {

            scope.$watch( '__height', function( newHeight, oldHeight ) {
                elem.attr( 'style', 'margin-top: ' + (58 + newHeight) + 'px' );
            } );
        }
    }
} )

/*
 * Checks every $digest for height changes
 */
.directive( 'emHeightSource', function() {

    return {
        link: function( scope, elem, attrs ) {

            scope.$watch( function() {
                scope.__height = elem.height();
            } );
        }
    }

} )
like image 102
AlwaysALearner Avatar answered Nov 14 '22 16:11

AlwaysALearner


You can monitor height change of your element without using Div, just writing a $watch statement:

// Observe the element's height.
scope.$watch
    (
        function () {
            return linkElement.height();
        },
        function (newValue, oldValue) {
            if (newValue != oldValue) {
                // Do something ...
                console.log(newValue);
            }
        }
    );
like image 43
Dor Cohen Avatar answered Nov 14 '22 16:11

Dor Cohen


May be you should watch for $window' dimensions changes , something like:

.directive( 'emHeightSource', [ '$window', function(  $window ) {

    return {
        link: function( scope, elem, attrs ){

           var win = angular.element($window);
           win.bind("resize",function(e){

              console.log(" Window resized! ");
              // Your relevant code here...

           })
        }
    }    
} ] )
like image 14
Ivan Chernykh Avatar answered Nov 14 '22 16:11

Ivan Chernykh


I used a combination of $watch and resize event. I found without scope.$apply(); in resize event, the height changes on the element is not always picked up by $watch.

   link:function (scope, elem) {
        var win = angular.element($window);
        scope.$watch(function () {
                return elem[0].offsetHeight;
        },
          function (newValue, oldValue) {
              if (newValue !== oldValue)
              {
                  // do some thing
              }
          });

        win.bind('resize', function () {
            scope.$apply();
        });
    };
like image 6
Adamy Avatar answered Nov 14 '22 16:11

Adamy