web-dev-qa-db-ja.com

AngularJSで孤立したスコープなしでディレクティブからコントローラー関数を呼び出す

孤立したスコープを使用せずに、ディレクティブ内から親スコープの関数を呼び出す方法を見つけることができないようです。分離スコープを使用する場合、分離で「&」を使用して親スコープの関数にアクセスできることを知っていますが、必要のない場合に分離スコープを使用すると結果が生じます。次のHTMLを検討してください。

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

この簡単な例では、JavaScript確認ダイアログを表示し、確認ダイアログで「OK」をクリックした場合にのみ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が機能しない上記の例です。 (このフィドルでは、入力ボックスに「yes」と入力すると、ボタンが非表示になることに注意してください。)

代替手段は、隔離されたスコープを使用しないことです。これは、このディレクティブのスコープを隔離する必要がないため、実際にここで本当に欲しいものです。私が持っている唯一の問題は、孤立したスコープに渡さない場合、親スコープのメソッドをどのように呼び出すのですか?

こちらはjsfiddleです 私は孤立したスコープを使用しておらず、ng-hideは正常に機能していますが、もちろん、confirmAction()の呼び出しは機能せず、どのようにそれを機能させるために。

、私が本当に探している答えは、孤立したスコープを使用せずに外側のスコープで関数を呼び出す方法であることに注意してください。そして、私は作ることに興味がありませんこの確認ダイアログは別の方法で機能します。この質問のポイントは、外部スコープを呼び出す方法を理解し、他のディレクティブを親スコープに対して機能させることができるためです。

また、他のディレクティブがまだ親スコープに対して機能する場合、孤立したスコープを使用するソリューションについて聞いてみたいと思いますが、私は思いませんこれは可能です。

94
Jim Cooper

ディレクティブは関数を呼び出すだけであり(プロパティに値を設定しようとしない)、$ parseの代わりに $ eval を使用できます(非分離スコープで)。

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

または、単に $ apply を使用するだけです。これにより、引数がスコープに対して$ eval()uされます。

scope.$apply(attrs.confirmAction);

フィドル

115
Mark Rajcok

AngularJSの$ parseサービスを利用したい。

あなたの例では:

.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を使用してプロパティを設定することとの間に落とし穴がありますが、私はあなたがそれに来たときにその橋を渡らせます。

17
Clark Pan

親スコープで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>
12
Jason

私が持っている唯一の問題は、孤立したスコープでそれを渡さない場合、どのように親スコープでメソッドを呼び出すのですか?

これは、孤立したスコープを使用しておらず、ng-hideが正常に機能しているjsfiddleですが、もちろん、confirmAction()の呼び出しが機能せず、機能させる方法がわかりません。

まず小さな点:この場合、外部コントローラーのスコープはディレクティブのスコープの親スコープではありません。外部コントローラーのスコープisディレクティブのスコープ。つまり、ディレクティブで使用される変数名は、コントローラーのスコープで直接検索されます。

次に、このボタンの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]();
      }
    });
  }
1
7stud