$ одновременно наблюдать несколько атрибутов и обратный вызов огня только один раз
Интересно, можно ли выполнить какой-либо обратный вызов только один раз после оценки всех (или только некоторых) атрибутов директивы (без изолированной области). Атрибуты действительно хороши для передачи конфигурации в директиву. Дело в том, что вы можете наблюдать каждый атрибут отдельно и обратный вызов огня несколько раз.
В примере у нас есть директива без изолированной области видимости, которая учитывает два атрибута: имя и фамилия. После любого изменения action
выполняется обратный вызов:
HTML
<button ng-click="name='John';surname='Brown'">Change all params</button>
<div person name="{{name}}" surname="{{surname}}"></div>
JS
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/>');
}
$attrs.$observe('name', action);
$attrs.$observe('surname', action);
}
}
});
Plunker здесь.
Таким образом, эффект заключается в том, что после смены имени и фамилии за один клик дважды срабатывает обратный вызов action
:
name:
surname: Brown
name: John
surname: Brown
Итак, вопрос: может ли action
быть запущен только один раз с измененными именами и фамилиями?
Ответы
Ответ 1
Вы можете использовать $watch
для оценки пользовательской функции, а не конкретной модели.
то есть.
$scope.$watch(function () {
return [$attrs.name, $attrs.surname];
}, action, true);
Это будет выполняться во всех циклах $digest
, и если $watch
обнаруживает возвращаемый массив (или, тем не менее, вы хотите структурировать возвращаемое значение функции), не соответствует старому значению, аргумент обратного вызова $watch
будет стрелять. Однако, если вы используете объект как возвращаемое значение, не забудьте оставить значение true
для последнего аргумента $watch
, чтобы $watch
выполнил глубокое сравнение.
Ответ 2
Подчеркивание (или lo-dash) имеет функцию once
. Если вы завершаете свою функцию внутри 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);
}
}
});
Ответ 3
Итак, я закончил свою собственную реализацию метода observeAll
, который может ждать нескольких изменений атрибутов в течение одного стека вызовов. Он работает, однако я не уверен в производительности.
Решение @cmw кажется более простым, но производительность может пострадать для большого количества параметров и нескольких фазовых циклов $digest, когда равенство объектов оценивается много раз. Однако я решил принять его ответ.
Ниже вы можете найти мой подход:
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);
}
};
}]);
И вы можете использовать его в своей директиве:
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 здесь
Ответ 4
Я решил ту же самую проблему, что у меня был другой подход, хотя я искал разные идеи. В то время как предложения cmw работают, я сравнивал его производительность с моим и видел, что метод $watch называется слишком много раз, поэтому я решил сохранить то, что я реализовал.
Я добавил $observ calls для обеих переменных, которые я хотел отслеживать, и привязал их к вызову debounce. Поскольку оба они модифицированы с очень малой разницей времени, оба метода $наблюдают за тем же вызовом функции, который запускается после короткой задержки:
var debounceUpdate = _.debounce(function () {
setMinAndMaxValue(attrs['minFieldName'], attrs['maxFieldName']);
}, 100);
attrs.$observe('minFieldName', function () {
debounceUpdate();
});
attrs.$observe('maxFieldName', function () {
debounceUpdate();
});
Ответ 5
Существует несколько способов решения этой проблемы. Мне очень понравилось решение для отладки. Однако, вот мое решение этой проблемы. Это объединяет все атрибуты в одном отдельном атрибуте и создает JSON-представление интересующих вас атрибутов. Теперь вам просто нужно соблюдать один атрибут и иметь хороший пертур тоже!
Вот вилка исходного plunkr с реализацией:
link
http://plnkr.co/edit/un3iPL2dfmSn1QJ4zWjQ