web-dev-qa-db-ja.com

あるAngularJSコントローラは別のコントローラを呼び出すことができますか?

あるコントローラに別のコントローラを使用させることは可能ですか?

例えば:

このHTMLドキュメントは、MessageCtrlコントローラによって配信されたメッセージをmessageCtrl.jsファイルに単純に出力します。

<html xmlns:ng="http://angularjs.org/">
<head>
    <meta charset="utf-8" />
    <title>Inter Controller Communication</title>
</head>
<body>
    <div ng:controller="MessageCtrl">
        <p>{{message}}</p>
    </div>

    <!-- Angular Scripts -->
    <script src="http://code.angularjs.org/angular-0.9.19.js" ng:autobind></script>
    <script src="js/messageCtrl.js" type="text/javascript"></script>
</body>
</html>

コントローラファイルには次のコードが含まれています。

function MessageCtrl()
{
    this.message = function() { 
        return "The current date is: " + new Date().toString(); 
    };
}

これは単に現在の日付を表示します。

特定のフォーマットの日付をDateCtrlに戻した別のコントローラMessageCtrlを追加する場合、どうすればよいでしょうか。 DIフレームワークはXmlHttpRequestsとサービスへのアクセスに関係しているようです。

567
BanksySan

コントローラ間の通信方法は複数あります。

最善の方法は、おそらくサービスを共有することです。

function FirstController(someDataService) 
{
  // use the data service, bind to template...
  // or call methods on someDataService to send a request to server
}

function SecondController(someDataService) 
{
  // has a reference to the same instance of the service
  // so if the service updates state for example, this controller knows about it
}

もう1つの方法は、スコープ上でイベントを発行することです。

function FirstController($scope) 
{
  $scope.$on('someEvent', function(event, args) {});
  // another controller or even directive
}

function SecondController($scope) 
{
  $scope.$emit('someEvent', args);
}

どちらの場合も、任意のディレクティブと通信できます。

694
Vojta

このフィドルを参照してください: http://jsfiddle.net/simpulton/XqDxG/

また、次のビデオを見てください: コントローラ間の通信

HTML:

<div ng-controller="ControllerZero">
  <input ng-model="message" >
  <button ng-click="handleClick(message);">LOG</button>
</div>

<div ng-controller="ControllerOne">
  <input ng-model="message" >
</div>

<div ng-controller="ControllerTwo">
  <input ng-model="message" >
</div>

javaScript:

var myModule = angular.module('myModule', []);
myModule.factory('mySharedService', function($rootScope) {
  var sharedService = {};

  sharedService.message = '';

  sharedService.prepForBroadcast = function(msg) {
    this.message = msg;
    this.broadcastItem();
  };

  sharedService.broadcastItem = function() {
    $rootScope.$broadcast('handleBroadcast');
  };

  return sharedService;
});

function ControllerZero($scope, sharedService) {
  $scope.handleClick = function(msg) {
    sharedService.prepForBroadcast(msg);
  };

  $scope.$on('handleBroadcast', function() {
    $scope.message = sharedService.message;
  });        
}

function ControllerOne($scope, sharedService) {
  $scope.$on('handleBroadcast', function() {
    $scope.message = 'ONE: ' + sharedService.message;
  });        
}

function ControllerTwo($scope, sharedService) {
  $scope.$on('handleBroadcast', function() {
    $scope.message = 'TWO: ' + sharedService.message;
  });
}

ControllerZero.$inject = ['$scope', 'mySharedService'];        

ControllerOne.$inject = ['$scope', 'mySharedService'];

ControllerTwo.$inject = ['$scope', 'mySharedService'];
120
adardesign

ここでは、サービスデータを共有する二つのコントローラの1ページの例です。

<!doctype html>
<html ng-app="project">
<head>
    <title>Angular: Service example</title>
    <script src="http://code.angularjs.org/angular-1.0.1.js"></script>
    <script>
var projectModule = angular.module('project',[]);

projectModule.factory('theService', function() {  
    return {
        thing : {
            x : 100
        }
    };
});

function FirstCtrl($scope, theService) {
    $scope.thing = theService.thing;
    $scope.name = "First Controller";
}

function SecondCtrl($scope, theService) {   
    $scope.someThing = theService.thing; 
    $scope.name = "Second Controller!";
}
    </script>
</head>
<body>  
    <div ng-controller="FirstCtrl">
        <h2>{{name}}</h2>
        <input ng-model="thing.x"/>         
    </div>

    <div ng-controller="SecondCtrl">
        <h2>{{name}}</h2>
        <input ng-model="someThing.x"/>             
    </div>
</body>
</html>

また、ここで: https://Gist.github.com/3595424

52
exclsr

あるコントローラを別のコントローラに呼び出したい場合は、4つの方法があります。

  1. $ rootScope。$ emit()および$ rootScope。$ broadcast()
  2. セカンドコントローラーが子供の場合は、親子コミュニケーションを使用できます。
  3. 利用サービス
  4. ハックの種類 - angular.element()の助けを借りて

1. $ rootScope。$ emit()および$ rootScope。$ broadcast()

コントローラとそのスコープは破壊される可能性がありますが、$ rootScopeはアプリケーション全体に残っています。そのため、$ rootScopeはすべてのスコープの親であるため、$ rootScopeを使用しています。

あなたが親から子へのコミュニケーションを実行していて、子供でさえその兄弟とコミュニケーションをとりたいなら、あなたは$ broadcastを使うことができます。

あなたが子供から親へのコミュニケーションを実行していて、兄弟が呼び出されていないなら、あなたは$ rootScopeを使うことができます。

HTML

<body ng-app="myApp">
    <div ng-controller="ParentCtrl" class="ng-scope">
      // ParentCtrl
      <div ng-controller="Sibling1" class="ng-scope">
        // Sibling first controller
      </div>
      <div ng-controller="Sibling2" class="ng-scope">
        // Sibling Second controller
        <div ng-controller="Child" class="ng-scope">
          // Child controller
        </div>
      </div>
    </div>
</body>

Angularjsコード

 var app =  angular.module('myApp',[]);//We will use it throughout the example 
    app.controller('Child', function($rootScope) {
      $rootScope.$emit('childEmit', 'Child calling parent');
      $rootScope.$broadcast('siblingAndParent');
    });

app.controller('Sibling1', function($rootScope) {
  $rootScope.$on('childEmit', function(event, data) {
    console.log(data + ' Inside Sibling one');
  });
  $rootScope.$on('siblingAndParent', function(event, data) {
    console.log('broadcast from child in parent');
  });
});

app.controller('Sibling2', function($rootScope) {
  $rootScope.$on('childEmit', function(event, data) {
    console.log(data + ' Inside Sibling two');
  });
  $rootScope.$on('siblingAndParent', function(event, data) {
    console.log('broadcast from child in parent');
  });
});

app.controller('ParentCtrl', function($rootScope) {
  $rootScope.$on('childEmit', function(event, data) {
    console.log(data + ' Inside parent controller');
  });
  $rootScope.$on('siblingAndParent', function(event, data) {
    console.log('broadcast from child in parent');
  });
});

上記のコードでは、$ emit 'childEmit'のコードコンソールは子兄弟の内側を呼び出さず、親の内側のみを呼び出します。ここで、$ broadcastは兄弟および親の内側でも呼び出されます。ここがパフォーマンスを発揮する場所です。あなたがそれがいくつかの汚れたチェックをスキップするので親から子へのコミュニケーションを使っているならば、好ましい。

2. 2番目のコントローラーが子供の場合は、Child Parent通信を使用できます _

あなたが直接の親と通信したいchild parent communicationを行いたいのであれば、それは最善の方法の1つです。親から子への通信を行いたい場合は、サービスまたは$ broadcastを使用する必要があります。

例えばHTML: -

<div ng-controller="ParentCtrl">
 <div ng-controller="ChildCtrl">
 </div>
</div>

角度

 app.controller('ParentCtrl', function($scope) {
   $scope.value='Its parent';
      });
  app.controller('ChildCtrl', function($scope) {
   console.log($scope.value);
  });

あなたが子供から親へのコミュニケーションを使用しているときはいつでも、Angularjsは子供の中の変数を探します。それが内側に存在しない場合、それは親コントローラの内側の値を見ることを選択します。

3.サービスを利用する

AngularJSは "懸念の分離" のサービスアーキテクチャを使用した概念をサポートしています。サービスはjavascript関数であり、特定のタスクのみを実行する責任があります。これはそれらを個々のエンティティである保守可能かつテスト可能にします。

Angularjsコード:

app.service('communicate',function(){
  this.communicateValue='Hello';
});

app.controller('ParentCtrl',function(communicate){//Dependency Injection
  console.log(communicate.communicateValue+" Parent World");
});

app.controller('ChildCtrl',function(communicate){//Dependency Injection
  console.log(communicate.communicateValue+" Child World");
});

こんにちは、こんにちは子供の世界とこんにちは親の世界を出力します。 Angular docs of services シングルトン - サービスに依存する各コンポーネントは、サービスファクトリによって生成された単一インスタンスへの参照を取得します

4.ハックの種類 - angular.element()の助けを借りて

このメソッドはId/uniqueによって要素からscope()を取得します。class.angular.element()メソッドはelementを返し、scope()は他の変数の$ scope変数を他のコントローラの$ scope変数を使って与えます。

HTML: -

<div id='parent' ng-controller='ParentCtrl'>{{varParent}}
 <span ng-click='getValueFromChild()'>Click to get ValueFormChild</span>
 <div id='child' ng-controller='childCtrl'>{{varChild}}
   <span ng-click='getValueFromParent()'>Click to get ValueFormParent </span>
 </div>
</div>

Angularjs: -

app.controller('ParentCtrl',function($scope){
 $scope.varParent="Hello Parent";
  $scope.getValueFromChild=function(){
  var childScope=angular.element('#child').scope();
  console.log(childScope.varChild);
  }
});

app.controller('ChildCtrl',function($scope){
 $scope.varChild="Hello Child";
  $scope.getValueFromParent=function(){
  var parentScope=angular.element('#parent').scope();
  console.log(parentScope.varParent);
  }
}); 

上記のコードでは、コントローラはHtmlに独自の値を表示しており、テキストをクリックするとコンソールに値が表示されます。親コントローラのスパンをクリックすると、ブラウザは子の値とその逆をコンソールに表示します。

51
Shubham Nigam

コントローラ間でデータを共有したり関数を呼び出したりするためにイベントを送信してブロードキャストする の場合は、この link :を見て、zbynourで答えを確認してください(最大票で回答)。私は彼の答えを引用しています!

firstCtrlのスコープがsecondCtrlスコープの親である場合、コードはfirstCtrl内の$ emitを$ broadcastに置き換えることで機能するはずです。

function firstCtrl($scope){
    $scope.$broadcast('someEvent', [1,2,3]);
}

function secondCtrl($scope){
    $scope.$on('someEvent', function(event, mass) {console.log(mass)});
}

スコープ間に親子関係がない場合は、$ rootScopeをコントローラに挿入して、イベントをすべての子スコープ(つまりsecondCtrl)にブロードキャストできます。

function firstCtrl($rootScope){
    $rootScope.$broadcast('someEvent', [1,2,3]);
}

最後に、イベントを子コントローラからスコープの上方へディスパッチする必要があるときは、$ scopeを使用できます。 firstCtrlのスコープがsecondCtrlスコープの親である場合:

function firstCtrl($scope){
    $scope.$on('someEvent', function(event, data) { console.log(data); });
}

function secondCtrl($scope){
    $scope.$emit('someEvent', [1,2,3]);
}
32
SharpCoder

さらに2つの問題:(非サービスアプローチ)

1)親子コントローラの場合 - イベントの発行/ブロードキャストに親コントローラの$scopeを使用します。 http://jsfiddle.net/laan_sachin/jnj6y/

2)関連しないコントローラ間で$rootScopeを使用する。 http://jsfiddle.net/VxafF/

24
DarkKnight

実際にはEmitとBroadcastを使用するのは効率的ではありません。イベントがスコープ階層を上下にバブルするため、複雑なアプリケーションのパフォーマンスが低下する可能性があります。

サービスを利用することをお勧めします。ここに私が最近私のプロジェクトの一つにそれを実装した方法があります - https://Gist.github.com/3384419

基本的な考え方 - pub-sub/eventバスをサービスとして登録するその後、イベント/トピックを購読または発行する必要がある場所に、そのイベントバスを注入します。

16
numan salati

これが規格外であるかどうかはわかりませんが、すべてのコントローラが同じファイルにある場合は、次のようにできます。

app = angular.module('dashboardBuzzAdmin', ['ngResource', 'ui.bootstrap']);

var indicatorsCtrl;
var perdiosCtrl;
var finesCtrl;

app.controller('IndicatorsCtrl', ['$scope', '$http', function ($scope, $http) {
  indicatorsCtrl = this;
  this.updateCharts = function () {
    finesCtrl.updateChart();
    periodsCtrl.updateChart();
  };
}]);

app.controller('periodsCtrl', ['$scope', '$http', function ($scope, $http) {
  periodsCtrl = this;
  this.updateChart = function() {...}
}]);

app.controller('FinesCtrl', ['$scope', '$http', function ($scope, $http) {
  finesCtrl = this;
  this.updateChart = function() {...}
}]);

ご覧のとおり、indicatorCtrlはupdateChartsを呼び出すときに他の両方のコントローラのupdateChart関数を呼び出しています。

3
tomascharad

私もこの方法を知っています。

angular.element($('#__userProfile')).scope().close();

角度コードではjQueryセレクターを使用したくないので、あまり使用しません。

3
Andrey Korchak

サービスに依存しないメソッド、$broadcastまたは$emitがあります。すべての場合に適しているわけではありませんが、ディレクティブに抽象化できる関連コントローラが2つある場合は、ディレクティブ定義でrequireオプションを使用できます。これが、ngModelとngFormの通信方法です。これを使用して、ネストされている、または同じ要素にあるディレクティブコントローラ間で通信できます。

親子関係の場合、使用は次のようになります。

<div parent-directive>
  <div inner-directive></div>
</div>

それを機能させるための主なポイント:親ディレクティブで、呼び出されるメソッドで、($scopeではなく)thisで定義する必要があります。

controller: function($scope) {
  this.publicMethodOnParentDirective = function() {
    // Do something
  }
}

子ディレクティブの定義では、requireオプションを使用して、親コントローラがリンク関数に渡されるようにすることができます(したがって、子ディレクティブのscopeからその関数を呼び出すことができます。

require: '^parentDirective',
template: '<span ng-click="onClick()">Click on this to call parent directive</span>',
link: function link(scope, iElement, iAttrs, parentController) {
  scope.onClick = function() {
    parentController.publicMethodOnParentDirective();
  }
}

上で見ることができます http://plnkr.co/edit/poeq460VmQER8Gl9w8Oz?p=preview

兄弟ディレクティブも同様に使用されますが、両方のディレクティブは同じ要素に対して使用されます。

<div directive1 directive2>
</div>

directive1にメソッドを作成することによって使用されます。

controller: function($scope) {
  this.publicMethod = function() {
    // Do something
  }
}

そしてdirective2では、requireオプションを使ってこれを呼び出すことができ、その結果siblingControllerがリンク関数に渡されます。

require: 'directive1',
template: '<span ng-click="onClick()">Click on this to call sibling directive1</span>',
link: function link(scope, iElement, iAttrs, siblingController) {
  scope.onClick = function() {
    siblingController.publicMethod();
  }
}

これは http://plnkr.co/edit/MUD2snf9zvadfnDXq85w?p=preview で見ることができます。

これの用途は?

  • 親:子要素が自分自身を親に「登録」する必要がある場合。 ngModelとngFormの関係によく似ています。これらは、モデルに影響を与える可能性がある特定の動作を追加する可能性があります。純粋にDOMベースのものもあるかもしれません。そこでは、親要素が特定の子の位置を管理する必要がある、例えばスクロールを管理する、またはそれに反応する必要があります。

  • 兄弟姉妹:ディレクティブの動作を変更できるようにします。 ngModelは古典的なケースで、入力でのngModelの使用にパーサー/検証を追加します。

2
Michal Charemza

あなたはあなたの親コントローラ(MessageCtrl)に '$ controller'サービスを注入し、それから次のように子コントローラ(DateCtrl)をインスタンス化/注入することができます。
$scope.childController = $controller('childController', { $scope: $scope.$new() });

これはサービスであるため、そのメソッドを呼び出すことで、子コントローラからデータにアクセスできます。
何か問題があれば教えてください。

2

以下はAngular JSとは無関係のpublish-subscribeアプローチです。

検索パラメータコントローラ

//Note: Multiple entities publish the same event
regionButtonClicked: function () 
{
        EM.fireEvent('onSearchParamSelectedEvent', 'region');
},

plantButtonClicked: function () 
{
        EM.fireEvent('onSearchParamSelectedEvent', 'plant');
},

検索選択コントローラ

//Note: It subscribes for the 'onSearchParamSelectedEvent' published by the Search Param Controller
localSubscribe: function () {
        EM.on('onSearchParamSelectedEvent', this.loadChoicesView, this);

});


loadChoicesView: function (e) {

        //Get the entity name from eData attribute which was set in the event manager
        var entity = $(e.target).attr('eData');

        console.log(entity);

        currentSelectedEntity = entity;
        if (entity == 'region') {
            $('.getvalue').hide();
            this.loadRegionsView();
            this.collapseEntities();
        }
        else if (entity == 'plant') {
            $('.getvalue').hide();
            this.loadPlantsView();
            this.collapseEntities();
        }


});

イベントマネージャ

myBase.EventManager = {

    eventArray:new Array(),


    on: function(event, handler, exchangeId) {
        var idArray;
        if (this.eventArray[event] == null) {
            idArray = new Array();
        } else { 
            idArray = this.eventArray[event];
        }
        idArray.Push(exchangeId);
        this.eventArray[event] = idArray;

        //Binding using jQuery
        $(exchangeId).bind(event, handler);
    },

    un: function(event, handler, exchangeId) {

        if (this.eventArray[event] != null) {
            var idArray = this.eventArray[event];
            idArray.pop(exchangeId);
            this.eventArray[event] = idArray;

            $(exchangeId).unbind(event, handler);
        }
    },

    fireEvent: function(event, info) {
        var ids = this.eventArray[event];

        for (idindex = 0; idindex < ids.length; idindex++) {
            if (ids[idindex]) {

                //Add attribute eData
                $(ids[idindex]).attr('eData', info);
                $(ids[idindex]).trigger(event);
            }
        }
    }
};

グローバル

var EM = myBase.EventManager;
1
Lijo

角度1.5では、これは次のようにして実現できます。

(function() {
  'use strict';

  angular
    .module('app')
    .component('parentComponent',{
      bindings: {},
      templateUrl: '/templates/products/product.html',
      controller: 'ProductCtrl as vm'
    });

  angular
    .module('app')
    .controller('ProductCtrl', ProductCtrl);

  function ProductCtrl() {
    var vm = this;
    vm.openAccordion = false;

    // Capture stuff from each of the product forms
    vm.productForms = [{}];

    vm.addNewForm = function() {
      vm.productForms.Push({});
    }
  }

}());

これは親コンポーネントです。これで私は私のproductForms配列に別のオブジェクトをプッシュする関数を作成しました - 注意 - これは私の例にすぎません、この関数は本当に何でも構いません。

requireを利用する別のコンポーネントを作成することができます。

(function() {
  'use strict';

  angular
    .module('app')
    .component('childComponent', {
      bindings: {},
      require: {
        parent: '^parentComponent'
      },
      templateUrl: '/templates/products/product-form.html',
      controller: 'ProductFormCtrl as vm'
    });

  angular
    .module('app')
    .controller('ProductFormCtrl', ProductFormCtrl);

  function ProductFormCtrl() {
    var vm = this;

    // Initialization - make use of the parent controllers function
    vm.$onInit = function() {
      vm.addNewForm = vm.parent.addNewForm;
    };  
  }

}());

ここで、子コンポーネントは、親コンポーネントの関数addNewFormへの参照を作成しています。これは、その後、HTMLにバインドされ、他の関数と同様に呼び出すことができます。

1
Katana24