Вызов функции контроллера из директивы без изолированной области в AngularJS


Я не могу найти способ вызвать функцию в родительской области из директивы без использования изолированной области. Я знаю, что если я использую изолированную область, я могу просто использовать "&" в изолированной для доступа к функции в родительской области, но использование изолированной области, когда это не нужно, имеет последствия. Рассмотрим следующий HTML:

<button ng-hide="hideButton()" confirm="Are you sure?" confirm-action="doIt()">Do It</button>

в этом простом примере я хочу показать диалог подтверждения JavaScript и вызвать doIt() только если они нажмут "OK" в подтверждении диалог. Это просто с помощью изолированной области. Директива будет выглядеть так:

.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 где я не использую изолированные scope и ng-hide работают нормально, но, конечно, вызов confirmAction() не работает, и я не знаю, как заставить его работать.

обратите внимание, что ответ, который я действительно ищу, заключается в том, как вызывать функции во внешней области без использования изолированной области. и я не заинтересован в том, чтобы этот диалог подтверждения работал по-другому, потому что смысл этого вопроса заключается в том, чтобы выяснить, как совершать вызовы во внешнюю область и все еще иметь возможность иметь другие директивы работают против родительской области.

кроме того, мне было бы интересно услышать о решениях, которые используют изолированную область, если другие директивы все еще будут работать против родительской области, но я не думаю, что это возможно.

4 94

4 ответа:

так как директива вызывает только функцию (и не пытается установить значение для свойства), вы можете использовать $eval вместо $ parse (с неизолированной областью видимости):

scope.$apply(function() {
    scope.$eval(attrs.confirmAction);
});

или лучше, просто использовать $apply, который будет $eval()uate его аргумент Против области:

scope.$apply(attrs.confirmAction);

Скрипка

вы хотите использовать сервис $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's scope.
                        fn(scope, {$event : e});
                    });
                }
            });
        }
    };
});

есть проблема с установкой свойства с помощью $parse, но я позволю вам пересечь этот мост, когда вы придете к нему.

явного вызова 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>

единственная проблема у меня есть, как я могу вызвать метод из родительской области если я не передам его в изолированную область?

вот jsfiddle, где я не использую изолированную область и ng-hide работает нормально, но, конечно, вызов confirmAction() не делает работа, и я не знаю, как заставить ее работать.

во-первых, небольшой момент: в этом случае область внешнего контроллера не является родительской областью действия директивы; внешний область действия контроллера и область действия директивы. Другими словами, имена переменных, используемые в директиве, будут просматриваться непосредственно в области контроллера.

далее писать!--4--> не работает, потому что 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]();
      }
    });
  }