web-dev-qa-db-ja.com

分離されたスコープを持つディレクティブから動作を公開する方法は?

ディレクティブからメソッドを公開するにはどうすればよいですか?データに属性を使用する必要があることはわかっていますが、dataではなくbehaviorを公開したいのです。親コントローラーが呼び出すことができるもの。

私のDOMが次のようになっているとしましょう:

<div ng-app="main">
    <div ng-controller="MyCtrl">
        <button ng-click="call()" >Call</button>
        <div id="container" my-directive> </div>
    </div>
</div>

JavaScript:

angular.module("main", []).controller("MyCtrl", function($scope) {
    $scope.call = function() {
        $scope.myfn();
    };
}).directive("myDirective", function() {
    return {
        // scope: {},
        controller: function($scope) {
            $scope.myfn = function() {
                console.log("myfn called");
            }
        }
    };
});

jsFiddle: http://jsfiddle.net/5gDjQ/7/

scopeがコメント化されている場合(つまり、ディレクティブにスコープが分離されていない場合)、問題なく機能します。ボタンを押すと、myfnが呼び出され、コンソールに記録されます。

scopeのコメントを外すとすぐに機能しなくなります。 myfnは子スコープで定義され、親が簡単に使用できません。

私の場合、親スコープを汚染することは悪い考えだと思います。私はそれを避けたいと思います。

では、ディレクティブから親コントローラーに関数を公開するにはどうすればよいですか?または:親コントローラーからディレクティブでメソッドを呼び出すにはどうすればよいですか?

34
Konrad Garus

これを行うには、コントローラーに双方向にバインドされたスコープ内に変数を設定します( '='を使用)。ディレクティブで関数をその変数に割り当てると、angularはバインディングを使用してコントローラー内の対応する変数を検索します。その変数は、コントローラーが呼び出すことができる関数を指します。

http://jsfiddle.net/GWCCr/

html:新しい属性に注意してください:

<div ng-app="main">
    <div ng-controller="MyCtrl">
        <button ng-click="call()" >Call</button>
        <div id="container" my-directive my-fn="fnInCtrl"> </div>
    </div>
</div>

js:

angular.module("main", []).controller("MyCtrl", function($scope) {
    $scope.call = function() {
        $scope.fnInCtrl();
    };
}).directive("myDirective", function() {
    return {
        scope: {
            myFn: '='
        },
        controller: function($scope) {
            $scope.myFn = function() {
                console.log("myfn called");
            }
        }
    };
});
26
Roy Truelove

ディレクティブの中に隠された関数を呼び出す方法を理解しようとするのではなく、自分で考えるべきだと思います:なぜディレクティブで定義された関数を呼び出したいのですか?

ディレクティブの一部の動作をトリガーするには、アプリケーションのユーザーがディレクティブ内からトリガーすることもできます

もしそうなら、行うべき明白でAngularyのことは、それに反応するはずのディレクティブを含むスコープでイベントをブロードキャストするにすることです。次に、ディレクティブはそのイベントをリッスンし、その機能を単独でトリガーします。

これには追加の利点があります。

  • 必要に応じて、イベントデータオブジェクトでパラメータを送信できます。
  • 複数のディレクティブで反応をトリガーできます(例:コレクションを形成する場合)
  • ネストされた各ディレクティブを通じて関数コールバックを渡さなくても、スコープ階層の深いディレクティブと通信できます(たとえば、ディレクティブが他のディレクティブ内の別のディレクティブ内にある場合など)。
  • ディレクティブの分離を壊さない

非常に単純な例を考えてみましょう:どこかからダウンロードされたランダムなインスピレーションを得た引用を表示するウィジェットがあるとします。また、見積もりを別の見積もりに変更するボタンもあります。

ディレクティブのテンプレートは次のとおりです。

<p>{{ quote }}</p>
<button ng-click="refreshQuote()"></button>

そしてここにディレクティブのコードがあります:

app.directive("randomQuote", function () {
  return {
    restrict: "E",
    scope: {},
    link: function (scope) {
      scope.refreshQuote = function () {
        scope.quote = ... // some complicated code here
      };
      scope.refreshQuote();
    }
  };
});

ディレクティブは完全に自己完結型であることに注意してください。それは分離スコープを持ち、引用フェッチをそれ自体で行います。

コントローラーからの見積もりも更新できるようにしたいとします。これは、コントローラーコードでこれを呼び出すのと同じくらい簡単です。

$scope.$broadcast("refresh-random-quote");

イベントハンドラーを追加するには、次のコードをディレクティブのlink関数に追加する必要があります。

scope.$on("refresh-random-quote", function () {
  scope.refreshQuote();
});

このようにして、コントローラーからディレクティブへの一方向の通信チャネルを作成しました。ディレクティブの分離を壊さず、ディレクティブがイベントをブロードキャストするコードのスコープ階層の深い階層にネストされている場合にも機能します。 。

16
DzinX

ディレクティブから親コントローラーに関数を公開するにはどうすればよいですか?
または:親コントローラーからディレクティブのメソッドを呼び出すにはどうすればよいですか?

まあ、私はあなたがこれをしようとするべきではないと思います(すなわち、コントローラの動作をディレクティブに結合する)が、もし必要なら...ここでそれを行うことができる一つの方法です:コントローラ関数をディレクティブに渡します。ディレクティブは、コントローラーにディレクティブ関数を通知するために呼び出すことができます:

<div id="container" my-directive cb="setDirectiveFn(fn)"></div>

directive("myDirective", function() {
    return {
       scope: { cb: '&' },
        controller: function($scope) {
            $scope.myfn = function() {
                console.log("myfn called");
            }
            $scope.cb({fn: $scope.myfn});
        }
    };
});

フィドル

7
Mark Rajcok

貢献するために、@ georgeawgは、Serviceを使用して仕事をするクールなソリューションをくれました。このようにして、同じページで複数のディレクティブを処理できます。

<html ng-app="myApp">
<head>
  <script src="https://opensource.keycdn.com/angularjs/1.6.5/angular.min.js"></script>
</head>
<body ng-controller="mainCtrl">
  <h1>^v1.6.0 ($postLink hook required)</h1>
  <my-directive name="sample1" number="number1"></my-directive>
  <my-directive name="sample2" number="number2"></my-directive>
</body>
<script>
  angular.module('myApp', [])
    .controller('mainCtrl', ['$scope', 'myDirectiveFactory', function ($scope, myDirectiveFactory) {
      $scope.number1 = 10
      $scope.number2 = 0
      this.$postLink = function () {
        myDirectiveFactory.get('sample2')
          .increment()
          .increment()
          .increment()
          .increment()
        myDirectiveFactory.get('sample1')
          .increment()
          .increment()
        myDirectiveFactory.get('sample2')
        .decrement()
      }
    }])
    .factory('myDirectiveFactory', function () {
      var instance = {}
      return {
        get: function (name) {
          return instance[name]
        },
        register: function (name, value) {
          return instance[name] = value
        },
        destroy: function (name) {
          delete instance[name]
        }
      }
    })
    .controller('myDirectiveCtrl', ['$scope', 'myDirectiveFactory', function ($scope, myDirectiveFactory) {
      $scope.name = $scope.name || 'myDirective'
      $scope.$on('$destroy', function () {
        myDirectiveFactory.destroy($scope.name)
      })
      var service = {
        increment: function () {
          $scope.number++
          return this
        },
        decrement: function () {
          $scope.number--
          return this
        }
      }
      myDirectiveFactory.register($scope.name, service)
    }])
    .directive('myDirective', [function () {
      return {
        controller: 'myDirectiveCtrl',
        restrict: 'E',
        scope: {
          number: '<',
          name: '@?'
        },
        template: '<p> {{ number }} </p>'
      }
    }])
</script>
</html>
1
saulsluz

AngularJS V1.7.1のリリース* 新しい ng-refディレクティブ を導入します。

ng-ref 属性は、現在のスコープでコンポーネントのコントローラーを公開するようAngularJSに指示します。これは、オーディオプレーヤーなどのコンポーネントにそのAPIを兄弟コンポーネントに公開させる場合に役立ちます。その再生と停止コントロールに簡単にアクセスできます。

詳細については、

1
georgeawg