Most of those rules come from Angular Style Guide (https://github.com/johnpapa/angular-styleguide/blob/master/a1/README.md).
In this document, we call angular things the different parts of an angular module: constants, variables, services, factories, controllers, directives, filters, and components.
https://github.com/johnpapa/angular-styleguide/blob/master/a1/README.md#iife
Use IIFE to enclose any code
https://github.com/johnpapa/angular-styleguide/blob/master/a1/README.md#style-y020
Because Angular 1 does not support namespaces, we have to namespace our services, controllers, directives... names. Our application prefix is esn. Moreover, any esn module should add a module prefix. This module prefix can be shortened, as it's already expensive to have 2 prefixes. Eg: esnCalEvent, esnUniMessage, ...
https://github.com/johnpapa/angular-styleguide/blob/master/a1/README.md#naming
We use folder-by-feature (https://github.com/johnpapa/angular-styleguide/blob/master/a1/README.md#folders-by-feature-structure) layout. Every angular thing (component, directive, controller, service, module config, router) is in its own file. By convention, we use the following file name: [dashed angular thing name].[angular thing type].js. Moreover, we do not make distinctions between services, values and factories. Examples:
.factory('esnCalCalendars') => calendars.service.js
.directive('esnChatMessageDisplay') => message-display.directive.js
We use [module name].routes.js for everything related to the frontend router configuration. We use [module name].config.js for everything else related to application configuration. We use [module name].run.js for everything related to the run part of the application. Finally, we use a layout folder for everything related to the more global application layout.
That gives us the following structure for a classic ESN module, namely 'linagora.esn.example' :
example.module.js
example.config.js
example.routes.js
example.run.js
somefeature/
somefeature/inbox.service.js
somefeature/inbox-view.component.js
somefeature/inbox-message.component.js
otherfeature/
otherfeature/folder-view.component.js
layout/
layout/inbox-sidebar.component.js
There should be a direct relationship between angular things and file names. As such, looking a component 'esnExampleInboxMessage', I'm sure that the component is in the example module, and its file is 'inbox-message.component.js'.
https://github.com/johnpapa/angular-styleguide/blob/master/a1/README.md#organizing-tests
Because it's likely that the unit test files will evolve when the source file evolve, puting them side by side decrease the folder lookup that the developer should do to get the test file out of the source file. Test files are named .spec.js. thus the previous layout becomes:
example.module.js
example.config.js
example.routes.js
example.run.js
somefeature/
somefeature/inbox-message.component.js
somefeature/inbox-message.component.spec.js
somefeature/inbox.service.js
somefeature/inbox.service.spec.js
somefeature/inbox-view.component.js
somefeature/inbox-view.component.spec.js
otherfeature/
otherfeature/folder-view.component.js
layout/
layout/inbox-sidebar.component.js
Let's imagine the module got 20 components... In that case, move the components in a components subfolder.
example.module.js
example.config.js
example.routes.js
example.run.js
somefeature/
somefeature/inbox.service.js
somefeature/inbox.service.spec.js
somefeature/components/inbox-message.component.js
somefeature/components/inbox-message.component.spec.js
somefeature/components/inbox-view.component.js
somefeature/components/inbox-view.component.spec.js
somefeature/components/...
otherfeature/
otherfeature/folder-view.component.js
layout/
layout/inbox-sidebar.component.js
The golden number in the style guide is 7 : when there is 7 or more of "angular thing" in a module, create a folder for the "angular thing".
https://github.com/johnpapa/angular-styleguide/blob/master/a1/README.md#named-vs-anonymous-functions https://github.com/johnpapa/angular-styleguide/blob/master/a1/README.md#function-declarations-to-hide-implementation-details
Always use the order: declaration, definition, implementation :
'use strict';
// declaration
.factory('esnExampleInbox', esnExampleInbox);
// definition
function esnExampleInbox(svc1, svc4) {
return {
load: load,
refresh: refresh
};
// implementation
function load() {
...
}
function refresh() {
...
}
}https://github.com/johnpapa/angular-styleguide/blob/master/a1/README.md#controlleras-view-syntax https://github.com/johnpapa/angular-styleguide/blob/master/a1/README.md#controlleras-with-vm
'use strict';
.directive('esnExampleStar', esnExampleStar);
function esnExampleStar(svc1, svc4) {
return {
controller: 'esnExampleStarController',
controllerAs: 'starCtl',
bindTocontroller: true,
link: link
};
function link(scope, elem, attrs) {
...
}
}As far as possible, embed controllers into the directive definition
'use strict';
.directive('esnExampleStar', esnExampleStar);
function esnExampleStar(svc1, svc4) {
return {
controller: esnExampleStarController,
controllerAs: 'starCtl',
bindTocontroller: true
};
}
function esnExampleStarController(chewing, gum) {
...
}We use the self keyword to keep a reference to the object context, so we won't use "vm" as stated in the guide, and stick to self.
In controllers, always expose bindable members (the things the controller is exposing) on top of the function:
'use strict';
.controller('esnExampleStarController', esnExampleStarController);
function esnExampleStarController(svc1, svc4) {
var self = this;
self.vote = vote;
self.votes = [];
function vote() {
...
}
}You'll still need the $scope when you want to listen on an event ($on), or watch ($watch) some variable.
https://github.com/johnpapa/angular-styleguide/blob/master/a1/README.md#defer-controller-logic-to-services https://toddmotto.com/rethinking-angular-js-controllers/
Make as dumb as possible controllers. Defer all logic and data holding into services.
Use components instead of directives whenever that's possible. Angular documentation (https://docs.angularjs.org/guide/component) is really clear as to when not using a component:
- when you need access to the DOM
- when your directive needs to be triggered by an attribute
As far as possible, embed component controllers into the component file
'use strict';
.component('esnExampleStar', {
controller: esnExampleStarController,
controllerAs: 'starctl',
templateUrl: '/some/where.html'
});
function esnExampleStarController(svc1, svc4) {
var self = this;
self.vote = vote;
self.votes = [];
function vote() {
...
}
}Use "Components as route templates" (https://docs.angularjs.org/guide/component) to define your routes.
// wrong
.state('yolo', {
templateUrl: '/some/where.html',
controller: 'someController'
})
// good
.state('yolo', {
template: '<some-component></some-component>'
})
Just a question : we planned to have an automatic taks to build a dist we full notation for angular dependencies if I recall well. Will this more complicated layout be handled well by ng-annotate or its equivalent ?