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:

Advertisements

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:

AngularJS, Karma and debugging unit tests in WebStorm or IntelliJ IDEA

Recently i had to debug few Javascript unit tests in WebStorm IDE and was wondering if it’ll be as easy of an experience as it is in case of Java and IntelliJ IDEA (where i originally come from).

WebStorm 6 doesn’t offer native Karma test runner support (ver 7 which is currently in EAP, does – details here), but using a NodeJS plug-in you can execute any kind of NodeJS application (Karma included).

 

OK, what we’ll need in this exercise is following:

  • One of two JetBrains IDE’s, either:
    • WebStorm (great for JavaScript code) or
    • IntelliJ IDEA (Java’s no. 1 IDE)
  • NodeJS plug-in installed in the IDE:
    • WebStorm comes having it already pre-installed
    • in case of IDEA (Ultimate version, because Community Edition doesn’t have the required JavaScript plug-in for it to work, see here) the plug-in can be downloaded from here.
  • NodeJS environment which can be downloaded from here.
  • Karma (old name Testacular) test runner installed (“npm install -g karma”) that allows running unit (or E2E) tests in one of these browsers:
    • Chrome
    • ChromeCanary
    • Firefox
    • Opera
    • Safari (only Mac)
    • PhantomJS
    • IE (only Windows)
  • Chrome/Firefox “JetBrains IDE Support” extension (required for debugging) that can be downloaded from here.

 

Installing the NodeJS plug-in in IntelliJ IDEA:

  • Open “Settings” dialog (File -> Settings… in the menu bar)
  • Select “Plugins” (under “IDE Settings”)
  • Click “Browse repositories…”
  • Click “Download and Install” on the “NodeJS” plug-in
  • Press “Restart” when asked, to restart the IDE

 

Configuring IDE to execute Karma test in NodeJS using the plug-in:

  • Open the Run/Debug Configuration dialog by selecting “Edit Configurations” in the Run area of the main toolbar of WebStorm.
  • Add the following two configurations (picture below):
    • “Karma Run”: to perform a “single run” of your unit tests.
    • “Karma Server”: to start Karma in “Continuous Integration” mode (automatic re-runs of your tests whenever files change).
  • Configure the “Karma Run” configuration:
    • Press the “+” button in the top-left of the “Run/Debug Configurations” dialog.
    • Select “Node.js” in the list
    • Fill in the following fields:
      • Name: enter “Karma Run”
      • Path to Node: absolute path to NodeJS executable (i.e. “c:\NodeJS\node.exe”)
      • Working Directory: absolute path of your AngularJS application (i.e. “C:\MyProjects\AngularApp”)
      • Path to Node App JS File: Should point to the (globally, ie. -g) installed “Karma” NodeJs executable (i.e. “C:\Users\…\AppData\Roaming\npm\node_modules\karma\bin\karma”)
      • Application Parameters: run karma.conf.js –single-run –no-auto-watch –reporters dots
    • Press “Apply”
  • Configure the “Karma Server” configuration:
    • Essentially take the same steps as while configuring “Karma Run”, changing only the following:
      • Name: enter “Karma Run”
      • Application Parametersstart karma.conf.js –no-single-run –auto-watch –reporters dots
  • Configure the “Karma Debug” configuration to allow debugging of Karma unit tests
    • Press the “+” button in the top-left of the “Run/Debug Configurations” dialog.
    • Select “JavaScript Debug -> Remote” in the list
    • Fill in the following fields:
      • Name: enter “Karma Debug”
      • URL to openhttp://localhost:8100/debug.html (port number depends on your configuration in karma.conf.js file (passed as an “Application Parameter” in the previous two configurations)
      • Browser: chose either Chrome or Firefox
      • Set the “Remote URL” field to point to “http://localhost:8100/base

        Karma Debug configuration in IntelliJ WebStorm

 

Finally, run your “Karma Server” configuration and while it’s working in the background (–auto-watch mode), set debugging breakpoints in your code and fire “Karma Debug” configuration.

That’s it. Hope this small guide turns out to be helpful.

 

Resources:

Yeoman, Karma and e2e testing

Imagine my surprise when i first looked at the Gruntfile.js of my newly generated angular app and found out that Yeoman is configuring by default only the karma:unit section, missing out the karma:e2e piece. I know the product is fairly new, but i would expect it to be a little more “mature” (spent quite some time figuring out why my e2e tests aren’t working and how to solve the problem).

 

Anyway, in order to run your e2e tests, you have to do the following:

  1. Update the Gruntfile.js
karma: {
    e2e: {
        configFile: 'karma-e2e.conf.js'
    },
    unit: { ... }
},
  1. Update the karma-e2e.conf.js
urlRoot = '/e2e/';
proxies = {
    // port has to be the same your web server is running on
    '/': 'http://localhost:9000'
};

 

Also, if you want Grunt to run your e2e tests without the need to manually run the web server first, you can additionally define the following task:

grunt.registerTask('test:e2e', function () {
    grunt.task.run([
        'clean:server',
        'concurrent:server',
        'connect:livereload',
        'open',
        'karma:e2e'
    ]);
});

 

(and “test:unit” task accordingly, for consistency sake):

grunt.registerTask('test:unit', [
    'karma:unit'
]);

AngularJS basics

I’ve been reading a great introductory book on AngularJS recently (authored by Brad Green and Shyam Seshadri) and thought i’ll share with you some of my notes (prior to going further with reading however, i strongly advise to watch few videos on the subject from the wonderful egghead.io website):

  • The role of the server:
    • Traditional Web Apps – create HTML pages by assembling components (header, content, footer, etc.), joining it with the data and then shipping it all over the wire to the browser to display.
    • AngularJS Apps – serve static resources (HTML templates, media, etc.) and data to the browser which takes care of assembling.
  • “By structuring your application with Angular, your application’s templates are kept separate from the data that populates them. The result of this is that these templates are cacheable. Only new data need to come down to the browser after the first load. Just as with JavaScript, images, CSS, and other resources, caching these templates can give your application even better performance.”
  • Model-View-Controller (MVC)
    • application architecture that separates the code between:
      • model – managing data (which is stored in object properties)
      • controller – managing the application logic (javascript classes)
      • view – presenting the data to the user (DOM)
    • “The view gets data from the model to display to the user. When a user interacts with the application by clicking or typing, the controller responds by changing data in the model. Finally, the model notifies the view that a change has occurred so that it can update what it displays.”
  • Data binding
    • “…just declare which parts of the UI map to which JavaScript properties and have them sync automatically.”
    • use double-curly syntax {{someText}} or ng-bind=”someText” directive to insert new content into an existing HTML template (with the double-curly syntax, on the very first page load of your application’s index.html, there’s a chance that your user will see the un-rendered template before Angular has a chance to replace the curlies with your data).
  • Dependency Injection (definition taken from Craig Walls “Spring in Action” 3rd ed.)
    • Any nontrivial application is made up of two or more classes that collaborate with each other to perform some business logic. Traditionally, each object is responsible for obtaining its own references to the objects it collaborates with (its dependencies). This can lead to code which is:
      • highly coupled
      • hard-to-test
      • difficult to reuse
      • difficult to understand
      • code that  typically exhibits “whack-a-mole” bug behavior (fixing one bug results in the creation of one or more new bugs)
    • With DI, on the other hand, objects are given their dependencies at creation time by some third party (usually specialized DI framework) that coordinates each object in the system. Objects aren’t expected to create or obtain their dependencies—dependencies are injected into the objects that need them.
  • Directives:
    • in AngularJS you can write your templates as HTML, because at the core of the framework there is a powerful DOM transformation engine included that lets you extend HTML’s syntax (ng-app, ng-controller, ng-model and similar directives).
  • To invoke AngularJS you must:
    • Load the angular.js library (eg. from Google’s CDN (content delivery network), ie.: https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js”)
    • Tell Angular which part of the DOM it should manage with the ng-app directive (eg. <html ng-app>)
  • Basic AngularJS app startup flow:
    • A user requests the first page of your application.
    • The user’s browser makes an HTTP connection to your server and loads the index.html page containing your template.
    • Angular loads into the page, waits for the page to be fully loaded, and then looks for ng-app to define its template boundaries.
    • Angular traverses the template and looks for directives and bindings. This results in registration of listeners and DOM manipulation, as well as fetching initial data from the server. The end result of this work is that the app is bootstrapped and the template is converted into view as a DOM.
    • You connect to your server to load additional data you need to show the user as needed.
  • Rationale behind writing “unobtrusive JavaScript” code (eg. not using click, mousedown, and other such inline event handlers in your HTML) and how AngularJS re-examine the problem:
    • Not everyone’s browser supports JavaScript. Let everyone see all of your content and use your app without needing to execute code in the browser.
      • this is no longer true as if you’re running a browser without JavaScript, you’re relegated to sites created in the ’90s
    • Some folks use browsers that work differently. Visually impaired folks who use screen-readers and some mobile phone users can’t use sites with JavaScript.
      • modern screen-readers have caught up. With proper use of ARIA semantic tags, you can make very rich UIs easily accessible. Mobile phones now run JavaScript on par with desktop machines.
    • Javascript works differently across different platforms. IE is usually the culprit here. You need to put in different event-handling code depending on the browser.
      • Angular has an equivalent in the form of ng-eventhandler=”expression” where eventhandler would be replaced by click, mousedown, change, and so on. AngularJS directives differ from their event handler predecessors in that they b

        ehave the same in every browser. Angular takes care of the differences for you.

    • These event handlers reference functions in the global namespace. It will cause you headaches when you try to integrate other libraries with functions of the same names.
      • Similarly to the previous point, AngularJS equivalent eventhandlers do not operate on the global namespace. The expressions you specify can only access functions and data that is in the scope of the element’s controller.
    • These event handlers combine structure and behavior. This makes your code more difficult to maintain, extend, and understand.
      • There’s a simple acid test we can use to figure out if our system suffers from this coupling: can we create a unit test for our app logic that doesn’t require the DOM to be present? In Angular, yes we can write controllers containing our business logic without having references to the DOM.
  • While angular is a client-side-only technology and it’s possible to create angular web apps that don’t require a back-end server at all, it is recommended to host the project files using a local web-server during development to avoid issues with security restrictions (sandbox) in browsers. The sandbox implementation varies between browsers, but quite often prevents things like cookies, xhr, etc to function properly when an html page is opened via file:// scheme instead of http://.

Starting with AngularJS

There’s a lot of places on the internet where you can go and read about AngularJS, but to set some framework for the journey i prepared a short list of most useful links.

  1. angularjs.org – Google’s superheroic JavaScript MVW Framework
  2. docs.angularjs.org/tutorial – great way to get introduced to AngularJS
  3. builtwith.angularjs.org – neat things built with AngularJS
  4. angular-ui.github.io – the companion suite(s)
  5. angular-ui.github.io/bootstrap – bootstrap components written in pure AngularJS
  6. ngmodules.org – angular modules
  7. github.com/…/angularjs-batarang – AngularJS WebInspector Extension for Chrome
  8. egghead.io – great video tutorial
  9. plus.google.com/+AngularJS – news on the framework
  10. youtube.com/angularjs – videos
  11. twitter.com/angularjs – tweets
  12. groups.google.com/…/angular – forum discussions
  13. github.com/…/JSFiddle-Examples – useful fiddles, gists, plunkers collected from forum discussions
  14. stackoverflow.com/…/angularjs – questions and solutions
  15. angularjs.meetup.com – meetup groups
  16. cheatography.com/…/angularjs – cheat-sheet