Вызовите функцию контроллера из директивы без изолированной области в AngularJS
Я не могу найти способ вызвать функцию в родительской области из директивы без использования изолированной области. Я знаю, что если я использую изолированный объем, я могу просто использовать "&". в изолированном доступе к функции в родительской области, но с использованием изолированной области, когда это не является необходимым, имеет последствия. Рассмотрим следующий HTML:
<button ng-hide="hideButton()" confirm="Are you sure?" confirm-action="doIt()">Do It</button>
В этом простом примере я хочу показать диалоговое окно подтверждения JavaScript и только вызвать doIt(), если они нажмут "ОК" в диалоговом окне подтверждения. Это просто, используя изолированную область. Директива будет выглядеть так:
.directive('confirm', function () {
return {
restrict: 'A',
scope: {
confirm: '@',
confirmAction: '&'
},
link: function (scope, element, attrs) {
element.bind('click', function (e) {
if (confirm(scope.confirm)) {
scope.confirmAction();
}
});
}
};
})
Но проблема в том, что, поскольку я использую изолированную область видимости, ng-hide в приведенном выше примере больше не выполняется против родительской области, а скорее в изолированной области (поскольку использование изолированной области по любой директиве, все директивы этого элемента используют изолированный объем). Ниже приведен пример jsFiddle, где ng-hide не работает. (Обратите внимание, что в этом скрипте кнопка должна скрываться при вводе "да" в поле ввода.)
Альтернативой может быть НЕ использовать изолированную область, которая на самом деле является тем, что я действительно хочу здесь, так как нет необходимости выделять эту область действия. Единственная проблема, с которой я столкнулся, - , как я могу вызвать метод в родительской области, если я не передаю его в изолированной области?
Вот jsfiddle, где я НЕ использую изолированную область, и ng-hide работает нормально, но, конечно, вызов confirmAction() не работает, и я не знаю, как заставить его работать.
Обратите внимание, что я действительно ищу, как вызвать функции во внешнем пространстве БЕЗ использования изолированной области. И мне не интересно, чтобы это подтвердило работу диалога по-другому, потому что суть этого вопроса состоит в том, чтобы выяснить, как совершать вызовы внешней области и по-прежнему иметь возможность работать с другими директивами в отношении родительской области.
В качестве альтернативы мне было бы интересно узнать о решениях, которые используют изолированную область действия, если другие директивы будут по-прежнему работать против родительской области, но я не думаю, что это возможно.
Ответы
Ответ 1
Поскольку директива вызывает только функцию (и не пытается установить значение для свойства), вы можете использовать $eval вместо $parse (с неизолированной областью):
scope.$apply(function() {
scope.$eval(attrs.confirmAction);
});
Или лучше просто просто используйте $apply, который будет $eval() uate его аргумент против области:
scope.$apply(attrs.confirmAction);
Fiddle
Ответ 2
Вы хотите использовать службу $parse в AngularJS.
Итак, для вашего примера:
.directive('confirm', function ($parse) {
return {
restrict: 'A',
// Child scope, not isolated
scope : true,
link: function (scope, element, attrs) {
element.bind('click', function (e) {
if (confirm(attrs.confirm)) {
scope.$apply(function(){
// $parse returns a getter function to be
// executed against an object
var fn = $parse(attrs.confirmAction);
// In our case, we want to execute the statement in
// confirmAction i.e. 'doIt()' against the scope
// which this directive is bound to, because the
// scope is a child scope, not an isolated scope.
// The prototypical inheritance of scopes will mean
// that it will eventually find a 'doIt' function
// bound against a parent scope.
fn(scope, {$event : e});
});
}
});
}
};
});
Существует вопрос, связанный с установкой свойства с помощью $parse, но я позволю вам пересечь этот мост, когда вы придете к нему.
Ответ 3
Явно вызовите hideButton
в родительской области.
Здесь скрипка: http://jsfiddle.net/pXej2/5/
И вот обновленный HTML:
<div ng-app="myModule" ng-controller="myController">
<input ng-model="showIt"></input>
<button ng-hide="$parent.hideButton()" confirm="Are you sure?" confirm-action="doIt()">Do It</button>
</div>
Ответ 4
Единственная проблема, с которой я столкнулся, - как я могу вызвать метод в родительской области если я не передам его в автономном режиме?
Вот jsfiddle, где я НЕ использую изолированную область и ng-hide работает нормально, но, конечно, вызов confirmAction() не и я не знаю, как заставить его работать.
Сначала небольшая точка: в этом случае область внешнего контроллера не является родительской областью области действия директивы; область внешнего контроллера - это область действия директивы. Другими словами, имена переменных, используемые в директиве, будут просматриваться непосредственно в области контроллера.
Затем запись attrs.confirmAction()
не работает, потому что attrs.confirmAction
для этой кнопки
<button ... confirm-action="doIt()">Do It</button>
- строка "doIt()"
, и вы не можете вызвать строку, например. "doIt()"()
.
Ваш вопрос действительно:
Как мне вызвать функцию, когда у меня есть ее имя в виде строки?
В JavaScript вы чаще всего вызываете функции с использованием точечной нотации, например:
some_obj.greet()
Но вы также можете использовать нотацию массива:
some_obj['greet']
Обратите внимание, как значение индекса является строкой. Итак, в вашей директиве вы можете просто сделать это:
link: function (scope, element, attrs) {
element.bind('click', function (e) {
if (confirm(attrs.confirm)) {
var func_str = attrs.confirmAction;
var func_name = func_str.substring(0, func_str.indexOf('(')); // Or func_str.match(/[^(]+/)[0];
func_name = func_name.trim();
console.log(func_name); // => doIt
scope[func_name]();
}
});
}