📜 ⬆️ ⬇️

Bold AngularJS for Team Development [1/2]

After reading Google's AngularJS Guidelines , I got the impression of its incompleteness, and it also often hinted at the profit from using the Closure library. They also stated , “We do not think that these recommendations apply equally well to all projects using AngularJS. We will be happy to see a community initiative for a more generic style guide, applicable for both small and large projects. ”

Based on personal experience with Angular, several speeches , as well as experience in team development, I present to you this bold style guide on syntax, code writing and application structure on AngularJS.

Module definition


Modules in AngularJS can be declared in various ways: either using a variable or via getter syntax. Always use the second method (moreover, it is recommended by the developers of the framework ).
')
Poorly:

var app = angular.module('app', []); app.controller(); app.factory(); 

Good:

 angular .module('app', []) .controller() .factory(); 

Functions and methods of the module


Angular modules have many different methods, such as controller , factory , directive , service , etc. There are also many different syntaxes for modules when it comes to DI and code formatting. Use the definition of named functions to later transfer their names to the appropriate methods. This way gives you more opportunity when tracing the stack, since functions are not anonymous (of course, you can just start using named functions instead of anonymous ones, but this way is more readable).

Poorly:

 var app = angular.module('app', []); app.controller('MyCtrl', function () { }); 

Good:

 function MainCtrl () { } angular .module('app', []) .controller('MainCtrl', MainCtrl); 

Define a module once using the setter syntax like this: angular.module('app', []) . Then, if you need to access this module (for example, in other files), use the getter syntax: angular.module('app') .

And in order not to pollute the global scope, simply wrap all your code in IIFE.

Fine:

 (function () { angular.module('app', []); // MainCtrl.js function MainCtrl () { } angular .module('app') .controller('MainCtrl', MainCtrl); // AnotherCtrl.js function AnotherCtrl () { } angular .module('app') .controller('AnotherCtrl', AnotherCtrl); //   ... })(); 

Controllers


In view of the fact that controllers are classes, in addition to the usual controller syntax, they have controllerAs syntax. Use it precisely because, in addition to being able to reference the controller instance, this method makes the scopes nested.

Binding to DOM through controllerAs


Poorly:

 <div ng-controller="MainCtrl"> {{ someObject }} </div> 

Good:

 <div ng-controller="MainCtrl as main"> {{ main.someObject }} </div> 

It is also worth noting that the method of binding to the DOM via the ng-controller attribute in many respects limits the use of this view (view) only in conjunction with the specified controller. And although rarely, there are still situations where the same representation can be used with different controllers. For more flexibility in this matter, use a router to connect the view with the controller.

Fine:

 <!-- main.html --> <div> {{ main.someObject }} </div> <!-- main.html --> <script> // ... function config ($routeProvider) { $routeProvider .when('/', { templateUrl: 'views/main.html', controller: 'MainCtrl', controllerAs: 'main' }); } angular .module('app') .config(config); //... </script> 

To avoid the use of $parent when you need to access any of the parent controllers, with this approach, we simply write the controllerAs value, in our case main . It is clear that due to this method, we also avoid calls like $parent.$parent .

this in controllerAs


The controllerAs syntax implies the use of the this in the controller code instead of $scope . When using controllerAs , the controller is actually tied to $scope , which, in fact, gives such a degree of separation.

Poorly:

 function MainCtrl ($scope) { $scope.someObject = {}; $scope.doSomething = function () { }; } angular .module('app') .controller('MainCtrl', MainCtrl); 

You can also use prototype to create controller classes, but this will quickly pollute the code, because each DI provider needs a corresponding reference in the constructor.

Good and bad:

Good for inheritance, but bad for general use.

 function MainCtrl ($scope) { this.someObject = {}; this._$scope = $scope; } MainCtrl.prototype.doSomething = function () { // use this._$scope }; angular .module('app') .controller('MainCtrl', MainCtrl); 

If you use prototype but do not know why, then this is bad. If you use prototype to isolate from other controllers, this is good. And for the general case, the use of prototype may simply be redundant.

Good:

 function MainCtrl () { this.someObject = {}; this.doSomething = function () { }; } angular .module('app') .controller('MainCtrl', MainCtrl); 

Above just shows an example of using objects and functions in the controller. Of course, this does not mean that we are going to use logic there ...

Avoid using logic in controllers.


Delegate logic to factories and services.

Poorly:

 function MainCtrl () { this.doSomething = function () { }; } angular .module('app') .controller('MainCtrl', MainCtrl); 

Good:

 function MainCtrl (SomeService) { this.doSomething = SomeService.doSomething; } angular .module('app') .controller('MainCtrl', MainCtrl); 

This approach maximizes code reuse, reliably encapsulates its functionality, and makes testing easier and more accurate.

Services


Services are instantiated, respectively, they must be class-like. That is why here we also use this , we make out the code of functions in accordance with everything else.

Good:

 function SomeService () { this.someMethod = function () { }; } angular .module('app') .service('SomeService', SomeService); 

Factories


Factories give us a singleton module to create service methods (such as, for example, connecting an application to a server via REST). Creating and returning on request a solid object maintains existing bindings (binds) in the controller updated, and also helps to avoid problems with the binding of primitives.

Important: In fact, “Factory” is a template / implementation and should not be identified with the provider. It would be more correct to call both factories and services “services”.

Poorly:

 function AnotherService () { var someValue = ''; var someMethod = function () { }; return { someValue: someValue, someMethod: someMethod }; } angular .module('app') .factory('AnotherService', AnotherService); 

Good:

First, we create the object of the same name inside the function, and then we fill it with methods. This contributes both to the manual documentation of the code and the generation of documentation by automatic means.

 function AnotherService () { var AnotherService = {}; AnotherService.someValue = ''; AnotherService.someMethod = function () { }; return AnotherService; } angular .module('app') .factory('AnotherService', AnotherService); 

Here, all the bindings to the primitives remain updated, and this also makes the intramodular organization of the namespace a little more simple to understand - here you can immediately see private methods and variables.

To be continued...


In the next part of the translation:

Continued "

This style guide is in the process of being finalized. Always up to date recommendations can be found on Github .

Source: https://habr.com/ru/post/235873/


All Articles