Hello!
In addition to developing our own
DBMS , our company is also engaged in custom development in a variety of areas, from harsh java-enterprise applications to small mobile applications. Our teams try to use advanced technologies and frameworks. And just as I am a representative of one of these teams. Today I want to share my development experience on
AngularJS and a couple of thoughts about designing a web application using this framework.

')
During the time that I have been developing, I have had to deal with various approaches to writing applications. Someone wraps simple things in very strange wrappers, so the author of the code has to further resort to "complementary decoupling of the explicable components" (c). There are people who, on the contrary, do not bother with architectural delights at all and write the code “here and now”, without worrying about the further maintenance and mental health of their colleagues. It seems to me that the code should still be moderately filled with abstractions, and at the same time it is easy to divide into logical modules and easy to read. Familiarity with AngularJS a couple of years ago brought an understanding of how it might look good in javascript.
Application Requirements
One can argue a lot about the merits and demerits of AngularJS, leave these disputes beyond the notes, dwell only on the main question - is it possible to use AngularJS in a serious application?
On the one hand, the framework imposes some restrictions on the structure of the application and introduces its own terms to describe it. The other structure and approaches of
many are baffled, but this, in my opinion, is a controversial minus, since any framework that solves such large-scale tasks somehow drives the architecture to itself. To write an application on one framework, and then easily transfer it to another - this is a typical “programmer utopia”. Therefore, the arguments on this point of many colleagues seem to me to be very far-fetched.
On the other hand, performance is the cornerstone of any single-page web application. In many ways, the performance of AngularJS depends on how deeply you plunge into it and how correctly you use certain components of the framework.
The creators of AngularJS do not conceal this and honestly tell how everything is arranged inside and how problems with speed can be avoided. The main point is that the framework is a tool, and each tool has a specific type of task.
So let's see which AngularJS tasks will
not help us solve:
- You have an application with a large number (thousands) of elements that are constantly added, deleted and moved on one page. This may be, for example, a game or an animation application.
- Your application operates on the client side with a large amount of “raw” data - it constantly transforms them, which forces them to change models and redraw their display accordingly.
- You have ready-made code written, for example, using JQuery and not literate, i.e. represents simply “noodles”. Aligning such code with AngularJS concepts may be too time consuming.
In other cases, I see no obstacles to using this framework in my projects. If you have more examples of unsuccessful use of AngularJS, give them in the comments, I will definitely add to this list.
Web application design.
We chose a tool, we decided that it suits our task, now let's proceed to the design.
To begin with, it is necessary to break all our functionality into modules and divide the responsibility of components. In practice, it comes down to building services, directives and controllers, as well as combining them into modules. Let's see what each of these basic entities means.
- A module is a container that stores components that solve one or several tasks.
- A service is a component that stores a reused code or object and allows you to select a common logic for the operation of other components. These can be operations on an object, data storage, cache, etc.
- A directive is a component that represents a reusable widget or a specific code for working with the browser's DOM tree and styles.
- The controller is a component containing specific logic (including UI logic) for the operation of a specific page or part of it.
Building the interaction of these components rests entirely on the programmer’s shoulders, the framework developers only give some
recommendations on how to build it. Unfortunately, this is, in my opinion, the main problem of the framework, because at this stage not everyone follows such guidelines and the wrong construction of the system begins, it becomes unsupported and the code (along with performance) turns into a nightmare.
I will not give a retelling of typical variants of building components, they are perfectly described by many people, for example,
here and
here .
I will cite several recommendations obtained by practical consideration how to build the interaction of our components as efficiently as possible.
- Constantly follow the code and do not be afraid to refactor it. I often come across the opinion “it works — don't touch it”, but this approach does not lead to the development of the project and, over time, the module begins to accumulate code that becomes uncontrollable. An example would be one complex controller of the main page, which sooner or later will swell up to the stage “I need a lot of hours to fix a bug.”
- If you have a complex directive that requires constant interaction with business logic (for example, an interactive map with labels and geolocation), create your own service for it. This service will work as an interface that can be transferred to other services or controllers, which greatly simplifies the code and encapsulates the interaction logic.
Exampleangular.module('googleMap', [])
- Actively use the internal directive controller to hide the logic of your widget. Instead of externally managing the directive, try removing all the “giblets” inside.
- Avoid controlling the DOM tree directly in the controller. Sometimes it seems easier than writing a separate directive, but to get structured code you need to follow this recommendation. In addition, AngularJS itself out of the box provides a large number of ready - made mini-directives that help in tasks like “show an element if ...” and “add a class if ...”
- Use double binding wisely, and where possible, use one-way binding. An overabundance of tracked variables can lead to a drop in speed. If you do not intend to change the variable, use one-way binding.
- If for any reason a direct connection between the controller, the service and the directive is not possible, use the “Observer” pattern ( Wikipedia and the AngularJS event bus ). Nevertheless, it is important not to abuse this, because you can register and receive an event in each $ scope of any controller. The excess of such constructions complicates the understanding and debugging of the code. For global system events, instead of using $ broadcast in a particular context, it is better to sign events on $ rootScope and initiate on it through $ rootScope. $ Emit. (thanks serf for addition)
- Avoid using the context root ($ rootScope), try to isolate the logic inside a single controller or controller-service bundle. The context root works on the entire application, so, for example, the tracking functions ($ watch) added there will be triggered during each cycle ($ digest) when the variable changes in different places of the application.
- Understand (if you haven't figured out yet) and use the promise mechanism (“promises”). This is a simple and visual way to get rid of spaghetti code when calling asynchronous functions. Also, one of the interesting applications of “promises” is a message to child controllers about the execution of asynchronous requests (through the context inheritance mechanism).
Example <div ng-controller="Controller1"> <div ng-controller="Controller2"></div> </div> ... function Controller1($scope, DataLoaderService) { ... // $scope.dataLoaded = DataLoaderService.get(...); ... } ... function Controller2($scope, DataLoaderService) { ... $scope.dataLoaded.then(function(result) { // Controller1 ... }); ... }
Some useful tools.
In addition to the recommendations, I would like to share useful tools for building an effective debugging and development process. In our work we actively use:
- WebStorm IDE . I think IDE needs no introduction, a simple and very easy-to-use environment from the guys at JetBrains . AngularJS support out of the box, including autosubstitution.
- JSDoc 3 . Documentation on a project is an important factor in its success, since well-documented code is easier to maintain. The standard for writing javascript documentation - JSDoc - has long been in effect and can be used to document the code of your AngularJS application. To generate beautiful html pages, you can use a special generator , it is simple and requires only Node.js.
- Jasmine . Javascript code can and should be tested. Unit testing is also possible in AngularJS, using the Jasmine framework and the Karma “starter”. Again, you will need Node.js, and setting up the whole environment should not take a lot of time and is described in detail with each tool.
- Closure Compiler . To speed up the load, the code can be minified using the javascript compiler, and in some places optimized. For AngularJS, Closure Compiler is great (by the way, AngularJS is going to be built by him ). A great guide on how to build your application is here . From myself I will only add that your application, alas, will not work in the ADVANCED_OPTIMIZATIONS mode .
- ng-annotate . Additional module for Node.js, which allows you to automatically add dependencies for injections in the code. As a result, you can get rid of excess code. (thanks anotherpit for the addition)
In conclusion, I want to say that if you have your own recommendations and convenient tools, I suggest discussing them in the comments and expanding this note.
Thanks for attention!
PS A note about Angular 1, I did not specifically mention Angular 2, since it is still in deep alpha, and so far the developers themselves do not recommend using it in real applications.