Angular.JS controller communication

AngularJS is a Javascript framework made by Google. Over the last few years, its popularity has increased exponentially. Its main features are double data binding, DOM control, form validation and the use of reusable components. Plus, it’s fairly easy to use, which makes AngularJS one of the top choices when looking for a Javascript framework.

When we start working with Angular, we come to this question very quickly: how can we set our controllers to communicate with each other? Which option is the best?

It is typically assumed that in Angular.js there should be only one controller per view. Sometimes, however, we have no choice but to have two controllers within the same page, and they will need to interact with each other.

Given that, I’ll go into some of the typical scenarios and how to handle the communication among controllers for each of them. I hope this is of help for some of you.

Father and Son

This is when we have a controller inside another controller. So let’s say that our markup looks like this:

JavaScript

1

2

3

4

5

6

7

8

9

10

<div ng-controller=”ParentController”>

<div>{{something}}</div>

<div ng-controller=”ChildController”>

<input type=”text”ng-model=”something”>

</div>

</div>

The problem would be, how can we access the model inside the child controller? If we want to get info already set in the other controller, then we could do something like:

JavaScript

1

2

3

‘$scope.something==$scope.$$childHead.message’or

‘$scope.something==$scope.$parent.message’

But this only works for reading the value that has been already set. If that value changes, our variable in the controller will not be updated. We could fix that with the following code:

1

2

3

4

5

$scope.$watch(‘$$childHead’,function(child,oldValue){

$scope.something=child.message

});

But watch out, an even better solution arrives! AngularJS provides us with some amazing functions that we can take advantage of:

1

$broadcast(name,args);

This function broadcasts an event to all children controllers. All the controllers that are listening for the ‘name’ event will get notified. The $broadcast function could be useful if you wanted to send information from the parent controller to a child controller.

1

$emit(name,args);

On the other hand, if what you wanted was to communicate from a child to its parent, we use this method:

1

$on(name,listener);

Finally, ‘on’ allows us to define which methods we are listening to. It listens for the ‘name’ event‘ emitted’ or ‘broadcasted’ methods depending on the case.

Communication from parent controller

Here’s an example:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

functionParentController($scope){

$scope.$watch('something',function(){

$scope.$broadcast('newMessage','I am your father');

});

}

functionChildController($scope){

$scope.$on('newMessage',function(event,msg){

alert(msg);

});

}

In this example, we are sending a message every time that our variable ‘something’ changes in the parent controller. Use of $watch is not necessary. What we need to keep in mind is that our child’s controllers and listener for the event needs to be defined before broadcasting

Communication from child controller

Here’s some markup example for this situation:

1

2

3

4

5

6

7

8

9

10

11

<div ng-controller="ParentController">

<input ng-model="message"/>

<div ng-controller="ChildController">

<button ng-click="tellToMyParent()">Say it</button>

</div>

</div>

And the code:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

functionParentController($scope){

$scope.$on('newMessage',function(event,msg){

alert(msg);

});

}

functionChildController($scope){

$scope.tellToMyParent=function(){

$scope.$emit('newMessage',$scope.message);

}

}

As you can see, the parent controller is listening for a ‘new message’ event. While in our child controller we are emitting this event every time we call ‘tellToMyParent’. We are calling that function when we click on the ‘Say it’ button, and it is sending the value of the $scope.message, which, in this case, is the value in the textbox.

Brother controllers

The idea sounds pretty good, right? Unfortunately sometimes this isn’t as easy as it sounds. There will be some situations on which we have brother controllers, and we can’t get them to communicate without the use of a parent controller.

Here’s some sample markup:

1

2

3

4

5

6

7

8

9

10

11

<div ng-controller=”ParentController”>

<div>{{something}}</div>

</div>

<div ng-controller=”SonController”>

<input type=”text”ng-model=”something”>

</div>

We know that there is one $rootScope for the Angular application and that we could take advantage of that. The code inside the controllers is interpreted in the child scope. And, if the property does not exist in the child scope, then Angular will look for the parent scope, ending at $rootScope. You can take advantage of this to help them communicate.

For example:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

functionFirstController($scope,$rootScope){

$rootScope.$on('newMessage',function(event,msg){

alert(msg);

});

}

functionSecondController($scope,$rootScope){

$scope.tellToMyBrother=function(){

$rootScope.$broadcast('newMessage',$scope.message);

}

}

Great, but there is only one thing left: if we use $rootScope.$on in the controller, this will create duplicate bindings. That is because controllers can get instantiated multiple times (every time the user enters the view, basically). So we need to be careful with that and find a way to avoid adding duplicate listeners, which could affect the application’s performance very drastically.

So, we still have one option left. That is, combining broadcast and services; services only get instantiated once, unlike controllers.

Examples:

First option; simple set and get:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

myModule.factory('myService',function($rootScope){

return{

message:'',

setMessage:function(msg){

this.message=msg;

},

getMessage:function(){

returnthis.message;

},

};

});

functionFirstController($scope,myService){

$scope.getMsg=function(){

alert(myService.getMessage());

};

};

functionSecondController($scope,myService){

$scope.sendMsg=function(msg){

myService.setMessage(msg);

};

};

Second option, combination with $broadcast:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

myModule.factory('myService',function($rootScope){

return{

broadcast:function(msg){

$rootScope.$broadcast('newMessage',msg);

},

};

});

functionFirstController($scope,myService){

$scope.$on('newMessage',function(event,msg){

alert(msg);

});

}

functionSecondController($scope,myService){

$scope.sendMsg=function(msg){

myService.broadcast(msg);

};

}

So, we have reviewed a few different ways of handling communication between controllers depending on the situation. As you can see, Angular.js is very flexible with this, but that does NOT mean that we should not to try to look for better implementations and follow best practices. What do you think about these options to communicate controllers? Do they seem useful? Have you found better ones? Feel free to share in the comments.