Tag Archives: AngularJS

Sharing data between controllers in AngularJS (PubSub/Event bus example)

Basically, there are two ways of handling the communication between controllers in AngularJS:

  • using a service which acts as a PubSub/Event bus when injected into controllers:
    • code example (John Lindquist’s fantastic webcast can be found here):
      'use strict';
      angular.module('myAppServices', [])
        .factory('EventBus', function () {
          return {message: "I'm data from EventBus service"}
        });
      
      'use strict';
      angular.module('myAppControllers', ['myAppServices'])
        .controller('FirstCtrl', function ($scope, EventBus) {
          $scope.data = EventBus;
        })
        .controller('SecondCtrl', function ($scope, EventBus) {
          $scope.data = EventBus;
        });
      

 

    • note:
      In case you don’t need a controller anymore on your page, there’s no way (other than manual) to automatically “unsubscribe” such controllers (as of today AngularJS doesn’t support component life-cycle hooks, by the use of which you could wire/un-wire components). This is because of closures used in controllers that are not “de-allocated” (memory) when the function returns. As a result, you’ll be still sending messages to such “unused” controllers.

 

  • depending on the parent/child relation between scopes, you can transmit events using either $broadcast or $emit methods:
    • if the scope of FirstCtrl is parent to the scope of SecondCtrl, you should use $broadcast method in the FirstCtrl:
      'use strict';
      angular.module('myAppControllers', [])
        .controller('FirstCtrl', function ($scope) {
          $scope.$broadcast('UPDATE_CHILD');
        })
        .controller('SecondCtrl', function ($scope) {
          $scope.$on('UPDATE_CHILD', function() {
            // do something useful here;
          });
        });
      

 

    • if there’s no parent/child relation between scopes, you should inject $rootScope into the FirstCtrl and broadcast the event into other controllers (including SecondCtrl) and their corresponding (child in this case) $scope’s:
      'use strict';
      angular.module('myAppControllers', [])
        .controller('FirstCtrl', function ($rootScope) {
          $rootScope.$broadcast('UPDATE_ALL');
        });
      

 

    • finally, when you need to dispatch the event from a child controller (SecondCtrl) to $scope’s upwards , you should use the $emit method:
      'use strict';
      angular.module('myAppControllers', [])
        .controller('FirstCtrl', function ($scope) {
          $scope.$on('UPDATE_PARENT', function() {
            // do something useful here;
          });
       })
        .controller('SecondCtrl', function ($scope) {
          $scope.$emit('UPDATE_PARENT');
       });
      

 

    • note:
      because $broadcast will dispatch events downwards through (all) scope’s hierarchy, it results in a slight performance hit (more details and performance tests results, here).

 

Cheers!

 

 

Resources:

Tricky behavior of AngularJS $resource service.

When using $resource service of AngularJS in one of the projects recently, i faced a tricky problem and thought it may be valuable to share the solution here.

 

Namely, one of the back-end services is returning an Array of String values like this, when making a GET call using a REST client:

[
  "Value_1",
  "Value_2",
  "Value_3",
  (...)
]

 

Having a standard AngularJS service defined like this:

angular.module('myAppBackendService', ['ngResource'])
  .factory('BackendApi', ['$resource', 'BackendHost', 'BackendPort', 'BackendVersion',
    function ($resource, BackendHost, BackendPort, BackendVersion) {
      var connString = BackendHost + ':' + BackendPort + '/' + BackendVersion;
      return {
        values: $resource(connString + '/values/:id',
        {
          id:'@id'
        }, {
          query: {method: 'GET', isArray: true},
          get: {method: 'GET', params:{id:'@id'}, isArray: true},
          save: {method: 'POST', isArray: true}
        })
      };
  }]);

 

and invoked like this

$scope.values = BackendApi.values.get(
  function(value) {
    // do something interesting with returned values here
    $log.debug('Success: Calling the /values back-end service', value);
  },
  function(errResponse) {
    // do something else in case of error here
    $log.debug('Error: Calling the /values back-end service', errResponse);
  });

 

i was getting a successful response from the server, however the data format which i was getting was completely unexpected to me:

[
  {
    "0" : "V",
    "1" : "a",
    "2" : "l",
    "3" : "u",
    "4" : "e",
    "5" : "_",
    "6" : "1"
  },
  {
    "0" : "V",
    "1" : "a",
    "2" : "l",
    "3" : "u",
    "4" : "e",
    "5" : "_",
    "6" : "2"
  }
]

you can imagine my surprise when trying to figure out what the heck was wrong with it?

 

After spending some time trying to google out a solution, i finally found the reason for such behavior. Listen to this:

“…ngResource expects an object or an array of objects in your response”

“…When isArray is set to true in the list of actions, the ngResource module iterates over each item received in the response and it creates a new instance of a Resource. To do this Angular performs a deep copy between the item received and the Resource class, which gives us an object with special methods ($save$deleteand so on)”

“…Internally angular uses angular.copy to perform the deep copy and this function only operates with objects andarrays, when we pass a string, it will treat it like an object.

Strings in JS can behave as arrays by providing sequential access to each character. angular.copy will produce the following when passed a string

angular.copy('hi',{}) => {0:'h', 1:'i'}

Each character becomes a value in an object, with its index set as the key. ngResource will provide a resource with properties 0 and 1.”

 

 

So, what are the possible solutions then?

  1. Use the “transformResponse” action of $resource service (you can read more about this in the documentation of the service itself, here)
  2. Use the lower level $http service:
    $http.get('/res').success(function(data){
      $scope.test = data;
    });
    
  3. Return an array of objects in your json response:
    [
      {'data': "hello"},
      {'data': "world"}
    ]
    

 

Happy coding!

 

 

 

 

Resources:

AngularJS custom HTTP headers in resource service

Recently i had to make an HTTP call from the browser (client-side) using JavaScript / AngularJS to a REST API (server-side) and retrieve data. Since the authentication mechanism of the API required a security token to be passed over with the request, i studied AngularJS specs on how to do it best. Basically, there are two ways to do it, either as a:

  1. query parameter, or
  2. custom HTTP header

 

Because i didn’t wanted the security token to appear anywhere in the logs or debugging console (like on the picture below, in case of making use of option 1 just mentioned, ie. query parameter), i decided on passing the token as a custom (there’s no standard header for passing tokens) HTTP header.

AngularJS query API token

 

Since i use Yeoman (app workflow/scaffolding tool) i noticed that through a standard angular-template used for generating an application scaffolding, you’re getting dependency on angular framework in version 1.0.7 (last stable version as of writing this post). Although this is what you would generally expect (stable version, not a snapshot), the problem is that angular documentation for $resource service (which is what i prefer to use over $http service), does not mention the possibility of sending HTTP headers (regarding $http – i think of it as a solution for rather “general purpose AJAX calls”).

 

One way to set HTTP headers is by accessing $httpProvider.defaults.headers configuration object, like this:

$httpProvider.defaults.headers.get['API-Token'] = 'vy4eUCqpQmGoeWsnHKwCQw'

(more documentation about that you’ll find here), but this way you’re modifying $httpProvider globally which may not be what you exactly want.

 

Google search came with help and i found issue 736, which acknowledges that “$resource should support custom http headers”, but it is with the (unstable) release 1.1.3 where this feature is supported for sure (maybe earlier “unstable” versions do support it too, haven’t checked that actually, but definitely none of the stable versions do, as of today).

 

So, what is it that you have to do in order to introduce an unstable version of AngularJS into your project managed by Bower?

bower install angular-unstable
bower install angular-resource-unstable

(dependency on angular-resource.js is required in order for it to work).

 

Now, the only other thing left to do is to update your index.html file accordingly (to make use of proper version of libraries) :

<script src="bower_components/angular-unstable/angular.js"></script>
<script src="bower_components/angular-resource-unstable/angular-resource.js"></script>

 

…and you can start adding custom HTTP headers in your code:

angular.module('usersService', ['ngResource'])
    .factory('User', function($resource, api-token) {
        var User = $resource('http://api.test.com\\:8080/1.0/users', { }, {
            query: {
                method: 'GET',
                isArray: true,
                headers: { 'API-Token': api-token }
            }
        });
        return User
    });

 

Hope this short post will save some of your time 🙂 Cheers!

 

 

Resources: