$observe multiple attributes at the same time and fire callback only once

前端 未结 5 1718
灰色年华
灰色年华 2021-02-19 20:38

I wonder is it possible to execute some callback only once after evaluation all (or only some) attributes of directive (without isolated scope). Attributes are really great to p

相关标签:
5条回答
  • 2021-02-19 21:15

    I resolved the exact same problem that I had using another approach, though I was looking for different ideas. While cmw's suggestions is working, I compared its performance against mine, and saw that the $watch method is called far too many times, so I decided to keep things the way I had implemented.

    I added $observe calls for both variables I wanted to track and bound them to a debounce call. Since they both are modified with very little time difference, both $observe methods trigger the same function call, which gets executed after a short delay:

    var debounceUpdate = _.debounce(function () {
        setMinAndMaxValue(attrs['minFieldName'], attrs['maxFieldName']);
    }, 100);
    
    attrs.$observe('minFieldName', function () {
        debounceUpdate();
    });
    
    attrs.$observe('maxFieldName', function () {
        debounceUpdate();
    });
    
    0 讨论(0)
  • 2021-02-19 21:18

    You can use $watch to evaluate a custom function rather than a specific model.

    i.e.

    $scope.$watch(function () {
      return [$attrs.name, $attrs.surname];
    }, action, true);
    

    That will be run on all $digest cycles, and if $watch detects the return array (or however you want to structure your function's return value) doesn't match the old value, the callback argument to $watch will fire. If you do use an object as the return value though, make sure to leave the true value in for the last argument to $watch so that $watch will do a deep compare.

    0 讨论(0)
  • 2021-02-19 21:21

    There are several ways presented to solve this problem. I liked the debounce solution a lot. However, here is my solution to this problem. This combines all the attributes in one single attribute and creates a JSON representation of the attributes that you are interested in. Now, you just need to $observe one attribute and have good perf too!

    Here is a fork of the original plunkr with the implementation: linkhttp://plnkr.co/edit/un3iPL2dfmSn1QJ4zWjQ

    0 讨论(0)
  • 2021-02-19 21:24

    So, I've ended up with my own implementation of observeAll method, which can wait for several changes of attributes during one call stack. It works however I'm not sure about performance.

    Solution of @cmw seems to be simpler but performance can suffer for large number of parameters and multiple $digest phase runs, when object equality is evaluated many many times. However I decided to accept his answer.

    Below you can find my approach:

    angular.module('utils.observeAll', []).
    
    factory('observeAll', ['$rootScope', function($rootScope) {
        return function($attrs, callback) {
            var o = {}, 
                callQueued = false, 
                args = arguments,
    
                observe = function(attr) {
                    $attrs.$observe(attr, function(value) {
                        o[attr] = value;
                        if (!callQueued) {
                            callQueued = true;
                            $rootScope.$evalAsync(function() {
                                var argArr = [];
                                for(var i = 2, max = args.length; i < max; i++) {
                                    var attr = args[i];
                                    argArr.push(o[attr]);
                                }
                                callback.apply(null, argArr);
                                callQueued = false;
                            });
                        }
                    });
                };
    
            for(var i = 2, max = args.length; i < max; i++) {
                var attr = args[i];
                if ($attrs.$attr[attr])
                    observe(attr);
            }
        };
    }]);
    

    And you can use it in your directive:

    angular.module('app', ['utils.observeAll']).
    
    directive('person', ['observeAll', function(observeAll) {
      return {
        restrict: 'A',
        link: function($scope, $elem, $attrs) {
            var action = function() {
              $elem.append('name: ' + $attrs.name + '<br/> surname: ' + $attrs.surname+'<br/><br/>');
            }
            observeAll($attrs, action, 'name', 'surname');
        }
     }
    }]);
    

    Plunker here

    0 讨论(0)
  • 2021-02-19 21:26

    Underscore (or lo-dash) has a once function. If you wrap your function inside once you can ensure your function will be called only once.

    angular.module('app', []).
    
    directive('person', function() {
      return {
        restrict: 'A',
        link: function($scope, $elem, $attrs) {
            var action = function() {
              $elem.append('name: ' + $attrs.name + '<br/> surname: ' + $attrs.surname+'<br/><br/>');
            }
            var once = _.once(action);
            $attrs.$observe('name', once);
            $attrs.$observe('surname', once);
        }
     }
    });
    
    0 讨论(0)
提交回复
热议问题