AngularJS : What is the best way to bind to a global event in a directive

后端 未结 5 659
野趣味
野趣味 2021-01-29 17:58

Imagine the situation in AngularJS where you want to create a directive that needs to respond to a global event. In this case, let\'s say, the window resize event.

What

5条回答
  •  夕颜
    夕颜 (楼主)
    2021-01-29 19:04

    When developing on top of a framework, I often find it helpful to think agnostically about a problem before designing an idiomatic. Answering the "what" and the "why" drives out the "how".

    The answer here really depends on the complexity of doSomethingFancy(). Is there data, a set of functionality, or domain object(s) associated with instances of this directive? Is it a purely presentational concern, like adjusting the width or height properties of certain elements to appropriate proportions of the window size? Make sure you're using the right tool for the job; don't bring the whole Swiss Army knife when the job calls for tweezers and you have access to a standalone pair. For sake of continuing in this vein, I'm going to operate with the assumption that doSomethingFancy() is a purely presentational function.

    The concern of wrapping a global browser event in an Angular event could be handled by some simple run phase configuration:

    angular.module('myApp')
        .run(function ($rootScope) {
            angular.element(window).on('resize', function () {
                $rootScope.$broadcast('global:resize');  
            })
        })
    ;
    

    Now Angular doesn't have to do all of the work associated with a directive on each $digest, but you're getting the same functionality.

    The second concern is operating on n number of elements when this event is fired. Again, if you don't need all of the bells and whistles of a directive, there are other ways to make this happen. You could expand or adapt the approach in the run block above:

    angular.module('myApp')
        .run(function () {
            angular.element(window).on('resize', function () {
                var elements = document.querySelectorAll('.reacts-to-resize');
            })
        })
    ;
    

    If you do have more complex logic that needs to happen on the resize event, it still does not necessarily mean that one or more directives are the best way to handle it. You could use a simple mediator service that gets instantiated instead of the aforementioned anonymous run phase configuration:

    /**
     * you can inject any services you want: $rootScope if you still want to $broadcast (in)
     * which case, you'd have a "Publisher" instead of a "Mediator"), one or more services 
     * that maintain some domain objects that you want to manipulate, etc.
     */
    function ResizeMediator($window) {
        function doSomethingFancy() {
            // whatever fancy stuff you want to do
        }
    
        angular.element($window).bind('resize', function () {
            // call doSomethingFancy() or maybe some other stuff
        });
    }
    
    angular.module('myApp')
        .service('resizeMediator', ResizeMediator)
        .run(resizeMediator)
    ;
    

    Now we have an encapsulated service that can be unit tested, but doesn't run unused execution phases.

    A couple concerns that would also factor into the decision:

    • Dead listeners - with Option 1, you're creating at least one event listener for every instance of the directive. If these elements are being dynamically added to or removed from the DOM, and you don't call $on('$destroy'), you're running the risk of event handlers applying themselves when their elements no longer exist.
    • Performance of width/height operators - I'm assuming that there is box-model logic here, given that the global event is the browser resize. If not, ignore this one; if so, you'll want to be careful about which properties you're accessing and how often, because browser reflows can be a huge culprit in performance degradation.

    It's likely that this answer is not as "Angular" as you were hoping for, but it's the way I'd solve the problem as I understand it with the added assumption of box-model-only logic.

提交回复
热议问题