web-dev-qa-db-fra.com

AngularJS: meilleure façon de surveiller le changement de hauteur

Je rencontre l'ancien problème de navigation à hauteur variable: A position: fixes navigation sur le dessus et contenu avec margin-top: $naviHeight au dessous de. La navigation peut changer de hauteur lorsque les données sont chargées de manière asynchrone. La marge du contenu doit donc évoluer en même temps.

Je veux que cela soit autonome. Donc, pas de code où les données sont chargées, mais seulement dans les éléments/directives HTML impliqués.

Actuellement, je le fais dans AngularJS 1.2.0 avec une minuterie comme celle-ci:

/*
* 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();
        }
    }

} ] )

Cela a l'évidence évidente inconvénient d'utiliser le minuteur (ce qui me semble un peu moche) et n certain délai après la navigation redimensionnée jusqu'à ce que le contenu soit déplacé.

Y a-t-il une meilleure manière de faire cela?

37
Scheintod

Cela fonctionne en enregistrant un observateur dans emHeightSource qui s'appelle tous les $digest. Il met à jour le __height propriété qui est à son tour surveillée dans 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();
            } );
        }
    }

} )
40
AlwaysALearner

Vous pouvez surveiller le changement de hauteur de votre élément sans utiliser Div, il vous suffit d'écrire un $watch déclaration:

// Observe the element's height.
scope.$watch
    (
        function () {
            return linkElement.height();
        },
        function (newValue, oldValue) {
            if (newValue != oldValue) {
                // Do something ...
                console.log(newValue);
            }
        }
    );
24
Dor Cohen

Peut-être devriez-vous faire attention à $window _ dimensions change, quelque chose comme:

.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...

           })
        }
    }    
} ] )
13
Cherniv

J'ai utilisé une combinaison de $ watch et de redimensionnement. J'ai trouvé sans portée. $ Apply (); Dans l'événement resize, les changements de hauteur sur l'élément ne sont pas toujours détectés par $ 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();
        });
    };
6
Adamy

Cette approche évite (potentiellement) de déclencher un reflow à chaque cycle de résumé. Il vérifie uniquement elem.height()/après/le cycle de résumé est terminé et ne génère un nouveau résumé que si la hauteur a changé.

var DEBOUNCE_INTERVAL = 50; //play with this to get a balance of performance/responsiveness
var timer
scope.$watch(function() { timer = timer || $timeout(
    function() {
       timer = null;
       var h = elem.height();
       if (scope.height !== h) {
           scope.$apply(function() { scope.height = h })
       }
    },
    DEBOUNCE_INTERVAL,
    false
)
4
Jamie Pate

J'ai écrit une autre variante basée sur un minuteur qui peut être liée à la portée, car, comme Jamie Pate l'a correctement déclaré, l'accès direct au DOM dans le cycle de synthèse n'est pas une très bonne idée.

.directive("sizeWatcher", ['$timeout', function ($timeout) {
    return {
        scope: {
            sizeWatcherHeight: '=',
            sizeWatcherWidth: '=',
        },
        link: function( scope, elem, attrs ){
            function checkSize(){
                scope.sizeWatcherHeight = elem.prop('offsetHeight');
                scope.sizeWatcherWidth = elem.prop('clientWidth');
                $timeout( checkSize, 1000 );
            }
            checkSize();
        }
    };
}

Maintenant, vous pouvez le lier sur n’importe quel élément:

<img size-watcher size-watcher-height="myheight">
<div style="height: {{ myheight }}px">

Ainsi, la div garde toujours (avec une latence d'une seconde) la même hauteur que l'image.

1
devsnd

Cette approche surveille la hauteur et la largeur des éléments et les affecte à une variable de la portée fournie dans l'attribut de vos éléments.

 <div el-size="size"></div>


.directive('elSize', ['$parse', function($parse) {
  return function(scope, elem, attrs) {
    var fn = $parse(attrs.elSize);

    scope.$watch(function() {
      return { width: elem.width(), height: elem.height() };
    }, function(size) {
      fn.assign(scope, size);
    }, true);

  }
}])
0
user3255557