[ACCEPTED]-Can one AngularJS controller call another?-angularjs

Accepted answer
Score: 710

There are multiple ways how to communicate 4 between controllers.

The best one is probably 3 sharing a service:

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
}

Another way is emitting 2 an event on scope:

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

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

In both cases, you can 1 communicate with any directive as well.

Score: 123

See this fiddle: http://jsfiddle.net/simpulton/XqDxG/

Also watch the following 1 video: Communicating Between Controllers

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'];
Score: 56

If you want to call one controller into 48 another there are four methods available

  1. $rootScope.$emit() and $rootScope.$broadcast()
  2. If Second controller is child ,you can use Parent child communication .
  3. Use Services
  4. Kind of hack - with the help of angular.element()

1. $rootScope.$emit() and $rootScope.$broadcast()

Controller 47 and its scope can get destroyed, but the 46 $rootScope remains across the application, that's 45 why we are taking $rootScope because $rootScope 44 is parent of all scopes .

If you are performing 43 communication from parent to child and even 42 child wants to communicate with its siblings, you 41 can use $broadcast

If you are performing 40 communication from child to parent ,no siblings 39 invovled then you can use $rootScope.$emit

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 38 Code

 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');
  });
});

In above code console of $emit 'childEmit' will 37 not call inside child siblings and It will 36 call inside only parent, where $broadcast 35 get called inside siblings and parent as 34 well.This is the place where performance 33 come into a action.$emit is preferrable, if 32 you are using child to parent communication 31 because it skips some dirty checks.

2. If Second controller is child, you can use Child Parent communication

Its one 30 of the best method, If you want to do child parent communication where 29 child wants to communicate with immediate parent then it 28 would not need any kind $broadcast or $emit 27 but if you want to do communication from 26 parent to child then you have to use either 25 service or $broadcast

For example HTML:-

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

Angularjs

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

Whenever 24 you are using child to parent communication, Angularjs 23 will search for a variable inside child, If 22 it is not present inside then it will choose 21 to see the values inside parent controller.

3.Use Services

AngularJS 20 supports the concepts of "Seperation of Concerns" using services 19 architecture. Services are javascript functions 18 and are responsible to do a specific tasks 17 only.This makes them an individual entity which is maintainable and testable.Services 16 used to inject using Dependency Injection 15 mecahnism of Angularjs.

Angularjs code:

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");
});

It 14 will give output Hello Child World and Hello 13 Parent World . According to Angular docs 12 of services Singletons – Each component dependent on a service gets a reference to the single instance generated by the service factory.

4.Kind of hack - with the help 11 of angular.element()

This method gets scope() from 10 the element by its Id / unique class.angular.element() method 9 returns element and scope() gives $scope 8 variable of another variable using $scope 7 variable of one controller inside another 6 is not a good practice.

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);
  }
}); 

In 5 above code controllers are showing their 4 own value on Html and when you will click 3 on text you will get values in console accordingly.If 2 you click on parent controllers span, browser 1 will console value of child and viceversa.

Score: 52

Here is a one-page example of two controllers 1 sharing service data:

<!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>

Also here: https://gist.github.com/3595424

Score: 33

If you are looking to emit & broadcast events to share data or call functions across controllers, please look at this link: and check the answer 2 by zbynour (answer with max votes). I am quoting 1 his answer !!!

If scope of firstCtrl is parent of the secondCtrl scope, your code should work by replacing $emit by $broadcast in firstCtrl:

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

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

In case there is no parent-child relation between your scopes you can inject $rootScope into the controller and broadcast the event to all child scopes (i.e. also secondCtrl).

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

Finally, when you need to dispatch the event from child controller to scopes upwards you can use $scope.$emit. If scope of firstCtrl is parent of the secondCtrl scope:

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

function secondCtrl($scope){
    $scope.$emit('someEvent', [1,2,3]);
}
Score: 24

Two more fiddles: (Non service approach)

1) For 3 Parent- Child controller - Using $scope of parent 2 controller to emit/broadcast events. http://jsfiddle.net/laan_sachin/jnj6y/

2) Using 1 $rootScope across non-related controllers. http://jsfiddle.net/VxafF/

Score: 17

Actually using emit and broadcast is inefficient 9 because the event bubbles up and down the 8 scope hierarchy which can easily degrade 7 into performance bottlement for a complex 6 application.

I would suggest using a service. Here 5 is how I recently implemented it in one 4 of my projects - https://gist.github.com/3384419.

Basic idea - register 3 a pub-sub/event bus as a service. Then inject 2 that event bus where ever you need to subscribe 1 or publish events/topics.

Score: 5

I also know of this way.

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

But I don't use 2 it too much, because I don't like to use 1 jQuery selectors in angular code.

Score: 3

There is a method not dependent on services, $broadcast or 35 $emit. It's not suitable in all cases, but if 34 you have 2 related controllers that can 33 be abstracted into directives, then you 32 can use the require option in the directive definition. This 31 is most likely how ngModel and ngForm communicate. You 30 can use this to communicate between directive 29 controllers that are either nested, or on 28 the same element.

For a parent/child situation, the 27 use would be as follows:

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

And the main points 26 to get it working: On the parent directive, with 25 the methods to be called, you should define 24 them on this (not on $scope):

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

On the child directive 23 definition, you can use the require option so the 22 parent controller is passed to the link 21 function (so you can then call functions 20 on it from the scope of the child directive.

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();
  }
}

The 19 above can be seen at http://plnkr.co/edit/poeq460VmQER8Gl9w8Oz?p=preview

A sibling directive 18 is used similarly, but both directives on 17 the same element:

<div directive1 directive2>
</div>

Used by creating a method 16 on directive1:

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

And in directive2 this can be called 15 by using the require option which results in the 14 siblingController being passed to the link 13 function:

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();
  }
}

This can be seen at http://plnkr.co/edit/MUD2snf9zvadfnDXq85w?p=preview .

The uses 12 of this?

  • Parent: Any case where child elements 11 need to "register" themselves 10 with a parent. Much like the relationship 9 between ngModel and ngForm. These can add 8 certain behaviour that can affects models. You 7 might have something purely DOM based as 6 well, where a parent element needs to manage 5 the positions of certain children, say to 4 manage or react to scrolling.

  • Sibling: allowing 3 a directive to have its behaviour modified. ngModel 2 is the classic case, to add parsers / validation 1 to ngModel use on inputs.

Score: 3

I don't know if this is out of standards 5 but if you have all of your controllers 4 on the same file, then you can do something 3 like this:

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() {...}
}]);

As you can see indicatorsCtrl 2 is calling the updateChart funcions of the 1 other both controllers when calling updateCharts.

Score: 3

You can inject '$controller' service in 5 your parent controller(MessageCtrl) and 4 then instantiate/inject the child controller(DateCtrl) using:
$scope.childController = $controller('childController', { $scope: $scope.$new() });

Now 3 you can access data from your child controller 2 by calling its methods as it is a service.
Let 1 me know if any issue.

Score: 1

Following is a publish-subscribe approach that is irrespective 1 of Angular JS.

Search Param Controller

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

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

Search Choices Controller

//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();
        }


});

Event Manager

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);
            }
        }
    }
};

Global

var EM = myBase.EventManager;
Score: 1

In angular 1.5 this can be accomplished 10 by doing the following:

(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({});
    }
  }

}());

This is the parent 9 component. In this I have created a function 8 that pushes another object into my productForms array 7 - note - this is just my example, this function 6 can be anything really.

Now we can create 5 another component that will make use of 4 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;
    };  
  }

}());

Here the child component is creating a 3 reference to the parents component function 2 addNewForm which can then be bound to the HTML and 1 called like any other function.

Score: 0

You can use $controller service provided by AngularJS.

angular.module('app',[]).controller('DateCtrl', ['$scope', function($scope){
  $scope.currentDate = function(){
    return "The current date is: " + new Date().toString(); 
  }
}]);

angular.module('app').controller('MessageCtrl', ['$scope', function($scope){

  angular.extend(this, $controller('DateCtrl', {
      $scope: $scope
  }));

  $scope.messageWithDate = function(message){
    return "'"+ message + "', " + $scope.currentDate;
  }

  $scope.action2 = function(){
    console.log('Overridden in ChildCtrl action2');
  }

}]);

0

More Related questions