2014-01-16 16 views
21

Wie kann ich benachrichtigt werden, wenn eine Direktive in der Größe geändert wird? Ich habe versuchtAngularJS - bind an Direktive resize

element[0].onresize = function() { 
      console.log(element[0].offsetWidth + " " + element[0].offsetHeight); 
     } 

aber es ist nicht die Funktion aufrufen

(function() { 
'use strict'; 

// Define the directive on the module. 
// Inject the dependencies. 
// Point to the directive definition function. 
angular.module('app').directive('nvLayout', ['$window', '$compile', layoutDirective]); 

function layoutDirective($window, $compile) { 
    // Usage: 
    // 
    // Creates: 
    // 
    var directive = { 
     link: link, 
     restrict: 'EA', 
     scope: { 
      layoutEntries: "=", 
      selected: "&onSelected" 
     }, 
     template: "<div></div>", 
     controller: controller 
    }; 
    return directive; 

    function link(scope, element, attrs) { 
     var elementCol = []; 

     var onSelectedHandler = scope.selected(); 

     element.on("resize", function() { 
      console.log("resized."); 
     }); 

     $(window).on("resize",scope.sizeNotifier); 

     scope.$on("$destroy", function() { 
      $(window).off("resize", $scope.sizeNotifier); 
     }); 

     scope.sizeNotifier = function() { 
      alert("windows is being resized..."); 
     }; 

     scope.onselected = function(id) { 
      onSelectedHandler(id); 
     }; 



     scope.$watch(function() { 
      return scope.layoutEntries.length; 
     }, 
     function (value) { 
      //layout was changed 
      activateLayout(scope.layoutEntries); 
     }); 

     function activateLayout(layoutEntries) { 


      for (var i = 0; i < layoutEntries.length; i++) { 

       if (elementCol[layoutEntries[i].id]) { 
        continue; 
       } 
       var div = "<nv-single-layout-entry id=slot" + layoutEntries[i].id + " on-selected='onselected' style=\"position:absolute;"; 
       div = div + "top:" + layoutEntries[i].position.top + "%;"; 
       div = div + "left:" + layoutEntries[i].position.left + "%;"; 
       div = div + "height:" + layoutEntries[i].size.height + "%;"; 
       div = div + "width:" + layoutEntries[i].size.width + "%;"; 
       div = div + "\"></nv-single-layout-entry>"; 

       var el = $compile(div)(scope); 
       element.append(el); 
       elementCol[layoutEntries[i].id] = 1; 
      } 


     }; 
    } 

    function controller($scope, $element) { 

    } 
} 

     })(); 
+0

habe ich versucht, den Zweck der Code zu verstehen, aber es ist mir schwer. Ich habe die JQuery-Abhängigkeit Ihres Codes beseitigt, und Sie können hier überprüfen: http://jsfiddle.net/U3pVM/2669/ –

+0

Ich versuche nur benachrichtigt zu werden, wenn das Element der Direktive in der Größe geändert wird. Ich kann auf der ganzen Seite hören, aber ich möchte wissen, wenn elemente verursachen die Richtlinie zu verschieben –

+0

@ li-raz: [meine Antwort] (http://stackoverflow.com/a/23031644/434989) erfüllt genau das :-) – epegzz

Antwort

7

Hier ein Beispielcode von dem, was Sie tun müssen:

APP.directive('nvLayout', function ($window) { 
    return { 
    template: "<div></div>", 
    restrict: 'EA', 
    link: function postLink(scope, element, attrs) { 

     scope.onResizeFunction = function() { 
     scope.windowHeight = $window.innerHeight; 
     scope.windowWidth = $window.innerWidth; 

     console.log(scope.windowHeight+"-"+scope.windowWidth) 
     }; 

     // Call to the function when the page is first loaded 
     scope.onResizeFunction(); 

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

Dies funktioniert nicht, wenn nur die Größe der Direktive geändert wird, aber nicht das Fenster selbst. – epegzz

+0

Bindung funktioniert für mich ... obwohl ich glaube nicht, dass Sie anrufen müssen –

14

Verwenden scope.$watch mit einer benutzerdefinierten Uhr Funktion:

scope.$watch(
    function() { 
    return [element[0].offsetWidth, element[0].offsetHeight].join('x'); 
    }, 
    function (value) { 
    console.log('directive got resized:', value.split('x')); 
    } 
) 
+1

Ich habe es versucht, aber es zeigt mir normalerweise alte Werte. Ich habe eine Sidebar, die mit einer Schaltfläche ausgeblendet oder angezeigt werden kann. Wenn ich auf klicke, passiert der Digest-Zyklus und die Watch-Funktion sagt mir, dass sich die Größe des Elements nicht ändert und dann die Größe des Elements geändert wird , die einen Fehler verursacht. Irgendwelche Ideen? –

+1

@HernanRajchert (und alle anderen zukünftigen Leser), denn _ [clientHeight] (https://developer.mozilla.org/en-US/docs/Web/API/Element.clientHeight) kann als CSS Höhe + berechnet werden CSS-Füllung - Höhe der horizontalen Bildlaufleiste (falls vorhanden) ._ Was Sie wollen, ist [.offsetHeight] (https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement.offsetHeight). Wenn Sie 'element [0] .offsetHeight' (und width) beobachten, sollte das funktionieren. –

+0

@JordanCarroll Für mich hat die Verwendung von offsetHeight anstelle von clientHeight das Problem nicht gelöst. – nardnob

2

Die einzige Möglichkeit, Größen- und Positionsänderungen an einem Element mithilfe von $ watch zu erkennen, besteht darin, dass Sie Ihren Bereich ständig mit $ interval oder $ timeout aktualisieren. Wenn es möglich ist, kann es zu einem teuren Vorgang werden und Ihre App verlangsamen.

Eine Möglichkeit, wie Sie eine Änderung an einem Element erkennen können, ist der Aufruf requestAnimationFrame.

var previousPosition = element[0].getBoundingClientRect(); 

onFrame(); 

function onFrame() { 
    var currentPosition = element[0].getBoundingClientRect(); 

    if (!angular.equals(previousPosition, currentPosition)) { 
    resiszeNotifier(); 
    } 

    previousPosition = currentPosition; 
    requestAnimationFrame(onFrame); 
} 

function resiszeNotifier() { 
    // Notify... 
} 

Hier ist ein Plunk demonstriert dies. Solange du die Box bewegst, bleibt sie rot.

http://plnkr.co/edit/qiMJaeipE9DgFsYd0sfr?p=preview

12

Sie würden wollen, typischerweise die offsetWidth und offsetHeight Eigenschaften des Elements zu beobachten. Bei neueren Versionen von AngularJS, können Sie $scope.$watchGroup in Ihrer Link-Funktion:

app.directive('myDirective', [function() { 

    function link($scope, element) { 
     var container = element[0]; 

     $scope.$watchGroup([ 
      function() { return container.offsetWidth; }, 
      function() { return container.offsetHeight; } 
     ], function(values) { 
       // Handle resize event ... 
     }); 
    } 

    // Return directive definition ... 

}]); 

Sie können jedoch feststellen, dass Updates sehr langsam sind, wenn das Element beobachtet Eigenschaften direkt auf diese Weise.

Um die Reaktionszeit Ihrer Anweisung zu erhöhen, können Sie die Aktualisierungsrate mithilfe von $interval moderieren. Hier ist ein Beispiel eines wiederverwendbaren Service für das Ansehen von Elementgrößen in einem konfigurierbaren Millisekunden Rate:

app.factory('sizeWatcher', ['$interval', function($interval) { 
    return function (element, rate) { 
     var self = this; 
     (self.update = function() { self.dimensions = [element.offsetWidth, element.offsetHeight]; })(); 
     self.monitor = $interval(self.update, rate); 
     self.group = [function() { return self.dimensions[0]; }, function() { return self.dimensions[1]; }]; 
     self.cancel = function() { $interval.cancel(self.monitor); }; 
    }; 
}]); 

Eine Richtlinie einen solchen Dienst mit so etwas wie folgt aussehen:

app.directive('myDirective', ['sizeWatcher', function(sizeWatcher) { 

    function link($scope, element) { 
     var container = element[0], 
      watcher = new sizeWatcher(container, 200); 

     $scope.$watchGroup(watcher.group, function(values) { 
      // Handle resize event ... 
     }); 

     $scope.$on('$destroy', watcher.cancel); 
    } 

    // Return directive definition ... 

}]); 

Hinweis der Aufruf an watcher.cancel() in der $scope.$destroy Ereignishandler; Dadurch wird sichergestellt, dass die $interval Instanz zerstört wird, wenn sie nicht mehr benötigt wird.

Ein JSFiddle-Beispiel kann here gefunden werden.

+3

Wer diese Methode verwenden möchte, sollte '$ interval (self.update, rate); 'mit' $ interval (self.update, rate, 0, false); 'ersetzen, was explizit besagt, dass $ apply am Ende nicht aufgerufen werden soll . Andernfalls wird der Digest für den Bereich bei jedem Callback-Funktionsaufruf aufgerufen. Sie können dies mit '$ scope überprüfen. $ Watch (function() {console.log (" invalidated ");});' –

0

Eine kleine Variation von Eliels Antwort funktionierte für mich. In dem directive.js:

 $scope.onResizeFunction = function() { 
     }; 

     // Call to the function when the page is first loaded 
     $scope.onResizeFunction(); 

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

Ich nenne

$(window).resize(); 

aus meinem app.js. Das d3-Diagramm der Direktive passt nun die Größe an, um den Container zu füllen.

0

Hier ist mein nehmen auf diese Richtlinie (mit Webpack als Bündler):

module.exports = (ngModule) -> 

    ngModule.directive 'onResize', ['Callback', (Callback) -> 
    restrict: 'A' 
    scope: 
     onResize: '@' 
     onResizeDebounce: '@' 
    link: (scope, element) -> 
     container = element[0] 
     eventName = scope.onResize || 'onResize' 
     delay = scope.onResizeDebounce || 1000 
     scope.$watchGroup [ 
     -> container.offsetWidth , 
     -> container.offsetHeight 
     ], _.debounce (values) -> 
     Callback.event(eventName, values) 
     , delay 
    ] 
Verwandte Themen