Thinking Angular

0
969

AngularJS is increasingly popular with web developers. We also confirm this by analyzing Google Trends search statistics. For example, the following chart shows the comparison of the number of Google searches involving some of the most popular JavaScript development frameworks:

However, the growing interest in this framework does not mean that it is effectively used on a large scale. The number of web applications developed with AngularJS is not currently proportional to the interest aroused. The reasons are to be found on the one hand to the relative youth of the project on the other hand to the not easy digestibility of the proposed programming model when it goes beyond the classic “Hello world”.

The learning curve

Usually, the first approach with AngularJS following a tutorial or workshop raises some enthusiasm. Seeing how with minimal instructions it is possible to already have a working prototype is a major stimulus to arouse the interest of the developer.

But often we stop at the surface and we imagine that everything is feasible with the four concepts learned during the presentation, but colliding with the real complexity of the framework. In fact, one thing that is not normally explicitly stated in AngularJS presentations is that it is a complex framework, for which effective mastery can serve months of use in the field.

In the absence of an awareness of the complexity of this framework, the learning curve is not linear, as evidenced by the semi-serious graph published by Ben Nadel on his blog:

Difference between framework and libraries

First of all, a distinction must be made between frameworks and libraries. It seems like a trivial thing, but often those who work with JavaScript are used to using libraries that simplify the management of one or more aspects of development.

For example, as is known, jQuery is a library that simplifies the manipulation of the DOM, while Underscore provides utility functions in the manipulation of arrays, objects, and other common problems, D3.js is a library specialized in graphics rendering of data.

The developer’s expectations are legitimate, but they do not take into account the fact that a certain logic must be followed, a certain way of reasoning, what is often referred to as the Angular way.

  • A library, therefore, is a set of functionalities that simplify the development of a particular programming problem;
  • On the contrary, a framework is more focused on providing an infrastructure for application development that offers functionality to solve a specific problem.

We can say that the main problem that a framework wants to solve is the organization of an application architecture.

Here, AngularJS is a JavaScript development framework with a particular propensity to support Single Page Application.

It is important to make this distinction between frameworks and libraries because very often AngularJS is compared with libraries like jQuery or similar ones with misleading results. Comparing AngularJS with a classic library is a symptom that you are starting off on the wrong foot, because you expect something that the framework does not provide.

Think in architectural terms

One of the most common mistakes is to think of using Angular to create HTML pages to add a little dynamism with JavaScript. This approach is hardly successful with Angular applications of a certain complexity. It is important to think that Angular applications (and, more generally, any Single Page Application ) are ” client-side applications ” and not “Web pages”.

And when creating a non-trivial application, its architecture is fundamental for maintainability.

AngularJS provides several elements to define the architecture of an application: controllers, services, directives, filters, etc. But all are based on a basic concept of the organization of the code: the concept of form.

One of best practice the JavaScript programming is to avoid using global variables. At the same time, using the modules allows us to follow this fundamental rule and to organize our code in reusable units.

The easiest way to create a form in AngularJS is as follows:

angular.module("mioModulo", []);

This statement creates an empty form and without dependencies. We can add to the module the various components that create our application, as in the following example:

angular.module("mioModulo")
     
    .controller("mioController", function() { 
         
        // ...
    })
     
    .service("mioServizio", function() {
     
        // ...
    });

It is important to note that it is not necessary to create the module and add elements in it to the same physical file. We can define a module and its contents in different files, de-coupling the correspondence between files and architectural elements of the application.

This aspect offers great freedom in organizing the code, but unfortunately, it can often be a source of disorientation.

Analyzing the code of your application well is one of the key success factors in developing an Angular application.

MVC or MVW … to each his task

We often talk about AngularJS as an MVC framework or, as one of its authors defined it, MVW ( Model-View-Whatever ), to underline the extreme flexibility in the application of a presentation pattern.

In reality, the support of the well-known design pattern is only one of Angular’s characteristics. In any case, the pattern defines the separation of tasks in the presentation of data to the user by identifying three fundamental components: the data model, the view and the controller or any other element that acts as a glue between the view and the data.

The task separation approach has several advantages, including the ability to internally modify a component without affecting the other.

Angular adopts the principle of separation of competencies extending it to the various components that contribute to the construction of an application. In particular, we can highlight the following main elements.

Element Description
View Represents what the user sees, the HTML resulting from the AngularJS processing
Controller It is the logic behind the View, the component that relates the data and their visualization
Service It is a component that offers functionality to other components, regardless of a View
Directive It is a reusable component within a View that extends HTML by adding custom attributes or elements
Filter Format the value of an expression for viewing on a View

 

In addition to these, there are other components that mainly have internal use or otherwise have very specific purposes. In any case, each component has a specific purpose and must be used for the purpose for which it was designed.

For example, one of the most frequent errors is to provide JavaScript instructions that manipulate the DOM within a controller. This often generates behaviors different from those expected, such as the failure to change the DOM, or even the occurrence of exceptions.

It is good to be clear that a controller is simply a support component to the view in the presentation of data. Its fundamental role is to provide data to the view through the scope possible data manipulation functionalities.

Any other functionality other than this must be delegated to the most appropriate component. For example, if we need to intervene on the DOM to create an effect on the view, then we must evaluate the creation of our own directive. But if we need to create a general reusable functionality from multiple components of our application, then we need to create a service.

Even for needs that might seem trivial, such as the definition of a constant globally accessible to all components of an application, requires the identification of the appropriate component. In the specific case it is necessary to create a particular type of service constant, as shown in the following example:

angular.module("mioModulo")
.constant("miaCostante", 123);

In short, understanding the role of each component is part of the construction of Angular thinking, it helps to create applications with the expected behavior and avoids errors that are sometimes difficult to diagnose.

Useful links

Starting from the following page we focus on Dependency Injection and other fundamental elements of the framework.

The separation of skills between the components of an application may appear a little too rigid at first sight. Having to create a specific component for a very simple functionality, such as can be a shared constant, can be seen as an unnecessary complication, and probably in some ways it is. But this is the price to pay for the advantage of modularization, ie the creation of components focused on a specific task and possibly reusable in different applications.

The composition of an application using different modules is obtained through the dependency injection pattern: another of the most difficult features to digest for those approaching AngularJS without a programming experience based on design patterns.

Most likely, the difficulty in understanding dependency injection derives directly from the unclear knowledge of the module concept.

In fact, dependency injection allows you to combine components together to structure an application. If within an Angular component we need the functionality offered by another component we have to do is declare its dependency.

For example, if in a controller we need to access the features offered by a service we can declare it as follows:

angular.module ( "MyForm")
    .controller ("mioController", function (myService) {
        // ...
    });

This is the classic example with which we declare the dependence of a controller on the system object $scopeor the service $httpto make Ajax calls:

angular.module("mioModulo")
    .controller("mioController", function($scope, $http) {
        //...
    });

Almost always, in fact, instead of the code of the previous example we see the following semantically equivalent code:

angular.module("mioModulo")
    .controller("mioController", ["$scope", "$http", function($scope, $http) {

        // ...
    }]);

The need to pass as a second argument of the constructor of a controller (or another component) an array of strings, whose last element is the function that implements the logic, is dictated by practical needs related to the minification of the code. In fact, if this precaution was not adopted, during the process that reduces the length of the name of the variables the references to the names of the components representing the dependencies would be lost.

This aspect is rarely explained to Angular neophytes who often find themselves using such a lengthy syntax without understanding why.

Finally, in addition to facilitating component structuring, the separation of skills and the dependency injectionsimplification of the code testability by isolating the various functionalities and preparing an infrastructure for the use of mock in the unit tests.

Declarative approach

Most JavaScript developers are used to handling the user interface by directly manipulating the DOM, possibly using libraries like jQuery, MooTools or others. So it is natural to think about how to modify a value displayed on a text box in terms of access to the DOM element properties.

AngularJS proposes a totally different approach in the manipulation of the user interface, a declarative approach based on the interpolation of expressions, on the use of directives and on the two-way data binding . The classic example is based on a view that contains the following code:

<pre class=”brush: php; html-script: true”>
The sum of <input type = “number” ng-model = “addend1” />
and <input type = “number” ng-model = “addend2″ />
</pre>

This example allows the user to enter two numerical values in the respective text boxes and obtain the relative sum. It is fully functional without writing a line of JavaScript.

The declarative approach in defining a view is very powerful and reduces the amount of code necessary to implement the required functionalities in a user interface. However, we must understand it well and be able to manage it appropriately.

Unfortunately, very often the availability of this approach that exploits expressions, directives and filters directly in the HTML is forgotten or underestimated by the developers. In these cases, the code added to the view controller tends to increase and becomes difficult to understand and manage.

As you can be necessary to define a view by reasoning on what and when to be displayed using the guidelines ng-show, ng-classetc. avoiding falling into the temptation to manipulate the DOM from inside the controller.

For example, when a view has items to be displayed or hidden based on certain conditions, you must identify these conditions and represent them using JavaScript expressions or scope variables, as in the following example:

<pre class=”brush: php; html-script: true”>
<form>
<input type=”text” ng-model=”cliente.nome”/>
<!– … –>
<input type=”button” value=”Salva” ng-click=”salva()”>
<input type=”button” value=”Elimina” ng-click=”elimina()” ng-show=”cliente.id > 0″>
</form>
</pre>

A customer data management form is displayed and the buttons for saving and deleting the customer are presented. The display of the delete button, however, is conditioned to the real existence of the customer object. That is, if we are in the process of creating a new customer, and therefore the value of his idis 0, the button is not displayed.

So, as far as possible, we take advantage of the declarative approach offered by Angular in defining a view by limiting the role of the relative controller essentially to the containment of the model and of data support functions. Keep in mind that if a controller code starts to become complex then something is wrong.

Events and internals in AngularJS

Most JavaScript programmers are used to thinking with an approach event-driven: every predictable event, generated by the user or by the system, must have its own manager.

AngularJS have certain guidelines to properly manage the user interaction with the GUI events: ng-click, ng-change, ng-mouseover, etc. In managing events, these directives must be used instead of trying to manage them according to the standard approach or worse with the jQuery approach.

In any case, before embarking on the management of an event, it is worth asking whether it is actually necessary. If a view has been well designed, it is often not necessary to resort to event management. For example, the impulse to use ng-changeto report on the data made on the user interface can be strong, but it can be superfluous since the two-way binding takes care to automatically maintain the correspondence of the values between the view and the data model.

The situation can get worse when you discover the ability to monitor a scope variable through $watch()or generate custom events through $broadcast()and $emit()manage them through $on(). The temptation to exploit them to bring Angular’s programming model to a known and comfortable as the event-driven one is strong, but the unthinked use of these tools often leads to building inefficient applications and sometimes with unpredictable behavior.

This does not mean that the management of events in Angular is always to be avoided. It is worth considering for good the need to resort to events or other functionalities that expose internal Angular mechanisms, analyzing first if the basic infrastructure of the framework does not already allow to manage the problem.

For example, often the temptation to resort to the generation and management of events is when you have to interact different controllers. For example, suppose you have the following code:

<pre class=”brush: php; html-script: true”>
<div ng-controller=”controller1″>
<input ng-model=”variabile1″/>
</div>
<div ng-controller=”controller2″>
<input ng-model=”variabile2″/>
</div>
</pre>

We want the value entered by the user on the first text box to also be displayed on the second box and vice versa. Since the two controllers have independent scopes, we could think of generating an event via the $broadcast()first controller and managing it via $on()the second controller and vice versa. This solution is certainly working, but it involves Angular’s internal infrastructure, since it has to be accessed $rootScope, and it can be inefficient.

The best solution is to create a service that acts as an intermediary between the two controllers, as shown by the following code:

angular.module("mioModulo")
    .factory("intermediario", function() {
        return { 
            obj: { 
                valore: ""
            }
        };
    });

At this point the two controllers can both refer to the property valoremade available by the service intermediarioand implicitly exploit the binding mechanism of Angular :

angular.module("mioModulo")
    
    .controller("controller1", ["$scope", "intermediario", function($scope, intermediario) {
    
        $scope.variabile1 = intermediario.obj;
    }])
    
    .controller("controller2", ["$scope", "intermediario", function($scope, intermediario) {
    
        $scope.variabile2 = intermediario.obj;
    }]);

The HTML then becomes in the following way:

<pre class=”brush: php; html-script: true”>
<div ng-controller=”controller1″>
<input ng-model=”variabile1.valore”/>
</div>
<div ng-controller=”controller2″>
<input ng-model=”variabile2.valore”/>
</div>
</pre>

In fact, the models of both text boxes point to the same object, obtaining the automatic update.

Of course, the point is not so much the specific example, but the approach itself. The event-based solution could be valid for the specific example, given its simplicity. But applied in more complex situations it can present problems, especially when the events generated and the elements involved are considerable in number.

The right attitude with AngularJS

Far from being exhaustive, this overview of the Angular vision of the development of Single Page Application wants to warn against an approach too easy to use the framework, without understanding the fundamentals well.

Using AngularJS while continuing to think that you have the JavaScript library used in the last project developed, risks turning a project into a failure. On the other hand, it is not said that the infrastructure proposed by AngularJS is necessary for our application. The choice of Angular should be carefully evaluated by analyzing the pros and cons, including the learning curve.

LEAVE A REPLY

Please enter your comment!
Please enter your name here