web-dev-qa-db-ja.com

AngularJSの親コントローラーからディレクティブのメソッドを呼び出す

エイリアスコントローラパターンでAngularJSを使用しています。親コントローラーからディレクティブメソッドにアクセスできません(または方法がわかりません)。

コントローラ内にディレクティブメソッドを呼び出す関数がありますが、このディレクティブメソッドはthisコントローラ値内では使用できません。

これは私が持っているものです。私が間違っているのは何ですか?

[〜#〜] js [〜#〜]

angular.module('myApp', []).

controller('MyCtrl', function(){
  this.text = 'Controller text';

  this.dirText = 'Directive text';

  this.click = function(){
    this.changeText();
  }
})

.directive('myDir', function(){
  return {
     restrict: 'E',
     scope: {
       text: '='
     },
     link: function(scope, element, attrs){
       scope.changeText = function(){
         scope.text = 'New directive text';
       };
     },
     template: '<h2>{{text}}</h2>'
  };
});

[〜#〜] html [〜#〜]

<div ng-app="myApp">
  <div ng-controller="MyCtrl as ctrl">
    <h1>{{ctrl.text}}</h1>
    <my-dir text="ctrl.dirText"></my-dir>
    <button ng-click="ctrl.click()">Change Directive Text</button>
  </div>
</div>

ここ コード付きのコードペン。

26
ianaya89

ディレクティブ内で分離されたscopeを厳密に使用する場合、angular $broadcast$emitなどのイベントを使用してのみ、ディレクティブメソッドを呼び出すことができます。

あなたの場合、$broadcastを使用して$rootScope全体にイベントを送信する必要があります

あなたのコードはこのようになります。

ワーキングコードペン

[〜#〜] html [〜#〜]

<div ng-app="myApp">
  <div ng-controller="MyCtrl as ctrl">
    <h1>{{ctrl.text}}</h1>
    <my-dir text="ctrl.dirText"></my-dir>
    <button ng-click="ctrl.click()">Change Directive Text</button>
  </div>
</div>

[〜#〜] code [〜#〜]

angular.module('myApp', []).

controller('MyCtrl', function($rootScope){
  var that = this;

  this.text = 'Controller text';

  this.dirText = 'Directive text';

  this.click = function(){
      $rootScope.$broadcast('changeText',{});
  }
}).

directive('myDir', function(){
  return {
     restrict: 'E',
     scope: {
       text: '='
     },
     link: function(scope, element, attrs){
       scope.changeText = function(){
         scope.text = 'New directive text';
       };
         scope.$on('changeText',function(event, data){
             scope.changeText()
         });
     },
     template: '<h2>{{text}}</h2>'
  };
});

子スコープのメソッドを呼び出す代わりに、イベントをブロードキャストする必要があります。イベントはディレクティブスコープでリッスンする必要があり、そのイベントをリッスンした後にchangeTextメソッドを起動します。

[〜#〜] note [〜#〜]

サービス/工場を使用する方が良いアプローチです。

これがうまくいけば助けになるでしょう。ありがとう。

29
Pankaj Parkar

$ broadcastに依存したり、スコープ分離を削除したりせずに、ディレクティブメソッドの呼び出しを実現できます。ここに投稿された同様のアプローチは、ページ上にディレクティブのインスタンスが2つ以上ある場合に壊れます(すべて同じ変更が反映されます)。

このcodepen は、より堅牢な方法を示しています。

angular.module('myApp', [])
.controller('myChat', function($scope) {
    
    function room () {return { accessor:{} }; }
    $scope.rooms = { 'RoomA': new room, 'RoomB': new room, 'RoomC': new room };

    $scope.addMessageTo = function(roomId, msg) {
      if ($scope.rooms[roomId].accessor.setMessage)
        $scope.rooms[roomId].accessor.setMessage(msg);
    };

    $scope.addMessages = function () {
      $scope.addMessageTo("RoomA", "A message");
      $scope.addMessageTo("RoomB", "Two messages");
      $scope.addMessageTo("RoomC", "More messages");
    }
    
}).directive('myChatRoom', function() {

    return {
      template: '<div>{{room}} message = {{message}}<div />',
      scope: { accessor: "=", room: "@" },
      link: function (scope) {
        if (scope.accessor) {
          scope.accessor.setMessage = function(msg) {
            scope.message = msg;
          };
        }
      }
    };
  
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
  <div ng-controller="myChat">

    <div ng-repeat="(roomId, room) in rooms">
      <div my-chat-room room="{{roomId}}" accessor="room.accessor"></div>
    </div>

    <button ng-click="addMessages()">Add messages to rooms</button>

  </div>
</div>
16
BernardV

別のソリューションを使用すると、分離スコープを使用でき、ブロードキャストに依存しません。 javascriptメソッドでは、変数のように使用できます。coを使用すると、単純に目的のメソッドをディレクティブに渡すことができます。

hTMLの場合:

<my-dir text="ctrl.dirText" change-text="ctrl.changeText"></my-dir>

およびディレクティブで

scope: {
   text: '=',
   changeText: '='
 }

ここ は、少し変更されたコードペンです。ここで、私が考えていることを確認できます。

11

あなたが書くとき、あなたはスコープを隔離しています:

 scope: {
       text: '='
     },

コードを少し変更したバージョンを次に示します。今回は、ディレクティブメソッドを呼び出すことができます。ほとんどの場合、'scope'ディレクティブで、これをコントローラーではなく$ scopeを使用するように変更し、別名パターンを追加しました。

警告:これは、どの変数が変更されるかに関して正しい動作を反映していない可能性がありますが、コントローラーからディレクティブのメソッドにアクセスする方法を示すことで質問に答えます。これは通常、良い設計案ではありません。

http://codepen.io/anon/pen/azwJBm

angular.module('myApp', []).

controller('MyCtrl', function($scope){
  var that = this;

  $scope.text = 'Controller text';

  $scope.dirText = 'Directive text';

  $scope.click = function(){
    $scope.changeText();
  }
}).

directive('myDir', function(){
  return {
     restrict: 'E',
    /* scope: {
       text: '='
     },*/
     link: function(scope, element, attrs){
       scope.changeText = function(){
         scope.text = 'New directive text';
       };
     },
     template: '<h2>{{text}}</h2>'
  };
});


<div ng-app="myApp">
  <div ng-controller="MyCtrl">
    <h1>{{text}}</h1>
    <my-dir text="dirText"></my-dir>
    <button ng-click="click()">Change Directive Text</button>
  </div>
</div>
2
Shaunak

$broadcastおよびcontrolオブジェクトソリューションでは、実際に値または配列をバインドすることをお勧めします。 controlオブジェクトは、目的の結果を得るための簡単な方法ですが、私のテストでは非常に発見できず、エラーが発生しやすくなっています。

このCodepenBernardV の例をビルドしますが、非常に目に見えるコントロールバインディングとしてメッセージの配列を使用します。必要に応じて、簡単に$watchディレクティブ内のメッセージ配列も同様です。コアのアイデアは、ディレクティブで使用することです:

scope: { messages: "=", room: "@" },

コントローラー(「部屋」の配列を持っている)から次のようにします。

$scope.addMessages = function () {
  angular.forEach($scope.rooms, function(room, index) {
    room.messages.Push("A new message! # " + (index+1);
  })
} 

独立したディレクティブ、独立したメッセージ、および非常に発見しやすい。もちろん、ディレクティブで最新のメッセージのみを表示することも、単に配列ではなく文字列をバインドすることもできます。このソリューションは、少なくとも私たちにとってはずっとうまくいきました。

0
Victor