Blog

Evaluation of AngularJS, a JavaScript UI Framework

Ilya Drabenia

During the last two months, our team has been using AngularJS, a JavaScript UI framework, to develop a user interface for the next-generation document management system. We also utilized Grunt for building a client application and Karma for automating unit tests. The back-end part was developed with the Java technology stack.

angularjs2

If you’re a novice to AngularJS, you can start with this introduction article. Below I’ll provide a short overview of the main AngularJS features we tried and give you some tips on how to fix some issues.

 

Rapid development

With this tool, JavaScript development becomes really fast. A single person can bring out 1–2 significant features a day. Not less important, AngularJS is also good for prototyping. This framework makes it is possible to find the right balance between a code quality and velocity of the development process.

 

Extending HTML capabilities with Directives

AngularJS comes with a set of built-in directives that allow for creating custom HTML elements and modifying the behavior of DOM elements. It can be quite useful for building complex front ends. Isolated scopes enable developers to divide data and use a particular portion of this data when necessary. One of the main documentation drawbacks is that it provides just a few examples of how to work with directives and scopes. So, we had to use these components intuitively.

However, you should be very careful while playing with them, since they may cause a lot of issues. For instance, the documentation says that a directive scope in AngularJS may be a child to the parent scope through the prototype-based JavaScript inheritance. In this case, parent scope variables will only be available for reading. Our team spent quite a lot of time investigating different parts of the application to find out how it really works. Eventually, we discovered a very useful tool for resolving issues like that: AngularJS WebInspector Extension for Chrome. It is a browser plug-in that allows for navigating through the hierarchy of AngularJS scopes.

 

Testing

Testing of applications in AngularJS is simple and intuitive.

a) Unit tests

We created the following unit tests to check the system’s models:

describe(‘Tabs control’, function() {
    var model;

    beforeEach(module(‘mycontrol’));
    beforeEach(inject(function(MyControlModel) {
         model = new MyControlModel();
    }));

    it(‘should allow to add new element’, function () {
         model.add({name: ‘Header’});
         expect(model.elements.length).toBe(1);
    });
});

 
b) Integration tests

AngularJS unit test libraries enable to create integration tests as well. As for our system, we developed a directive that represented a page and then triggered the events using jQuery. After that, the markup and state of the models were verified. We mocked the calls to the REST API with the “$httpBackend” service.

With this approach, smoke tests were completed just in five seconds. However, some specific features of Jasmine, a behavior-driven development framework for testing a JavaScript code, became a stumbling block for us. As it turned out, it does not allow for creating a single fixture per test suite. This feature is required to reduce the duration of tests on big components. To overcome this limitation, we created all tests in a single “it” function.

describe(‘Tabs control’, function() {
    var control;
    var scope;

    beforeEach(module(‘mycontrol’));
    beforeEach(inject(function($compile, $rootScope, $httpBackend) {
         // create custom directive with model
         scope = $rootScope.$new();
         control = $compile(‘<control></control>’)(scope);

         // mock some backend calls
         $httpBackend
             .when(‘GET’, ‘/api/resources’)
             .respond([{name: ‘name1’, data: ‘other_data’}]);
    }));

    it(‘should allow to add new element’, function () {
         $(control).find(‘button.add’).click();
         expect($(control).find(‘table tr’).length).toBe(1);
    });
});

 
c) End-to-end tests

As I mentioned in the beginning of the post, we used Grunt to build a client. It has the “watch” feature that starts tests, when some changes are made to the code. To test the application on servers that have no GUI, PhantomJS was used. All the technologies described above are very efficient together.

Probably, I should mention one more interesting AngularJS feature, black box testing of user interfaces with JavaScript. Even though this feature looks rather interesting, we decided to skip it. If we had used it, we would need to create a separate web page with the mocked back end to carry out these tests. In our case, it would make the back-end part of the system more complicated. Furthermore, we would be unable to distinctively separate the test code from the production code.

 

Filtering

AngularJS is very declarative and functional. Pure functions, closures, and the currying method are widely used when working with this framework.

The template engine introduces a new intriguing concept of filters It is something like map functions with the syntax inherited from Linux shell streams. With the two-way binding of scopes, this feature allows for implementing amazing features very fast. It is absolutely true, since filters enabled us to easily create lookups—re-usable lists of key-value pairs in drop-down menus.

filter(‘lookup’, [function () {
    var LOOKUPS = {
        USERS: {
            NEW: ‘New User’,
            EDIT: ‘Edit User’
        }
    };
    return function (input, lookupName) {
        return LOOKUPS[lookupName][input];
    }
}])

<div ng-init=”foo = ‘NEW’”> <!-- Key of lookup value -->
{{ foo | lookup:’USERS’ }} <!--  Will be rendered ‘New User’’ -->
</div>

 

Dependency injection

AngularJS has a built-in dependency injection (DI) engine. Compared to pure jQuery, AngularJS provides better isolation of modules: developers should spend less time reviewing the legacy code after adding new features. With this pattern, we were able to minimize the impact of the changed code on the entire system.

However, the DI engine may be not suitable in some cases. For instance, to create a constructor, we needed to develop a separate module, give it a name, and specify this name in the code each time the constructor was used. It’s much more time-consuming than just to create a namespace that will automatically insert a constructor everywhere we need it. We tried to find a way to bring this functionality to the AngularJS DI engine, but didn’t come across an acceptable option, unfortunately.

 

Form validation

Very often validators need customization. Since forms and validators are very flexible in AngularJS, you’ll be able to create an editable table with minimum efforts. Below is a simple example of a form validation:

<form ng-app="miniapp" name="miniform" novalidate> 
    <div ng-controller="Ctrl">
        <input name="field" type="text" ng-model="value" number="" />
        <span ng-show="miniform.field.$error.number">Field must contains number</span>
    </div>
</form>

angular.module('miniapp', [])

    .controller('Ctrl', function ($scope) {
        $scope.value = '';
    })

    .directive('number', function () {
        return {
            require: 'ngModel',
            link: function (scope, elm, attrs, ctrl) {
                ctrl.$parsers.unshift(function (viewValue) {
                    var isEmpty = viewValue === '';
                    var isNumber = !isNaN(parseInt(viewValue, 10));
                    ctrl.$setValidity('number', isEmpty || isNumber);
                    return viewValue;
                });
            }
        };
    });

You can play with this code on jsFiddle.

 

The template engine

Although the engine supports HTML tags, we experienced some issues with some of the tags while working with custom directives.

Another issue is that some standard AngularJS tags create new scopes, since a parent scope is only accessible while reading.

In addition, it’s not that easy to develop custom directives. AngularJS doesn’t support directive inheritance, as a result, the code is duplicated. Nevertheless, the composition of components looks really great.

 

Integration with jQuery

Sometimes it may be tricky to integrate AngularJS with jQuery plug-ins. We experienced memory leaks and spent a lot of time on fixing this. However, there is a way to easily eliminate these leaks. You just need to delete jQuery plug-in variables when deleting AngularJS scopes.

 

Compatibility with the earlier versions of IE

The front end of our system was developed for IE8. To provide support for earlier versions of IE with, we used the html5shiv script.

 

Conclusions

Being well architected, AngularJS speeds up front-end development and provides almost enterprise-level testing capabilities. This framework has better memory management than Backbone.js, supports form validation that Ember.js lacks, and allows for more flexible customization of components compared to Ext JS. With AngularJS, it will be easy for you to properly organize your web applications.

 
More on the topic: How to Speed Up AngularJS Apps That Use Internalization Libraries

3 Comments
  • thanks for the information

    ilcomsoft.com

  • ilya, did you ever experience timeout issues when rendering your snapshots with PhantomJS?

  • Palani Suresh

    Thank you its awesome artical of

Benchmarks and Research

Subscribe to new posts

Get new posts right in your inbox!