emmanueldemey / eslint-plugin-angular Goto Github PK
View Code? Open in Web Editor NEWESLint plugin for AngularJS applications
Home Page: https://www.npmjs.com/package/eslint-plugin-angular
ESLint plugin for AngularJS applications
Home Page: https://www.npmjs.com/package/eslint-plugin-angular
https://github.com/johnpapa/angularjs-styleguide/edit/master/README.md
Use controller as
syntax with a directive to be consistent with using controller as
with view and controller pairings.
Why?: It makes sense and it's not difficult.
Note: The directive below demonstrates some of the ways you can use scope inside of link and directive controllers, using controllerAs. I in-lined the template just to keep it all in one place.
Note: Regarding dependency injection, see Manually Identify Dependencies.
Note: Note that the directive's controller is outside the directive's closure. This style eliminates issues where the injection gets created as unreachable code after a return
.
<div my-example max="77"></div>
angular
.module('app')
.directive('myExample', myExample);
function myExample() {
var directive = {
restrict: 'EA',
templateUrl: 'app/feature/example.directive.html',
scope: {
max: '='
},
link: linkFunc,
controller: ExampleController,
controllerAs: 'vm',
bindToController: true // because the scope is isolated
};
return directive;
function linkFunc(scope, el, attr, ctrl) {
console.log('LINK: scope.min = %s *** should be undefined', scope.min);
console.log('LINK: scope.max = %s *** should be undefined', scope.max);
console.log('LINK: scope.vm.min = %s', scope.vm.min);
console.log('LINK: scope.vm.max = %s', scope.vm.max);
}
}
ExampleController.$inject = ['$scope'];
function ExampleController($scope) {
// Injecting $scope just for comparison
var vm = this;
vm.min = 3;
console.log('CTRL: $scope.vm.min = %s', $scope.vm.min);
console.log('CTRL: $scope.vm.max = %s', $scope.vm.max);
console.log('CTRL: vm.min = %s', vm.min);
console.log('CTRL: vm.max = %s', vm.max);
}
<!-- example.directive.html -->
<div>hello world</div>
<div>max={{vm.max}}<input ng-model="vm.max"/></div>
<div>min={{vm.min}}<input ng-model="vm.min"/></div>
If we can detect empty AngularJS controller, we can display warning to developers explaining that this Controller useless.
Based on the name of the file (configurable), we should be able to lint the Protractor Configuration file, in order to be sure to use the right properties
And service starting with '$' or '$$'
because angular.element() are already jqLite element
https://github.com/johnpapa/angularjs-styleguide/edit/master/README.md
Use function declarations to hide implementation details. Keep your accessible members of the factory up top. Point those to function declarations that appears later in the file. For more details see this post.
Why?: Placing accessible members at the top makes it easy to read and helps you instantly identify which functions of the factory you can access externally.
Why?: Placing the implementation details of a function later in the file moves that complexity out of view so you can see the important stuff up top.
Why?: Function declaration are hoisted so there are no concerns over using a function before it is defined (as there would be with function expressions).
Why?: You never have to worry with function declarations that moving var a
before var b
will break your code because a
depends on b
.
Why?: Order is critical with function expressions
/**
* avoid
* Using function expressions
*/
function dataservice($http, $location, $q, exception, logger) {
var isPrimed = false;
var primePromise;
var getAvengers = function() {
// implementation details go here
};
var getAvengerCount = function() {
// implementation details go here
};
var getAvengersCast = function() {
// implementation details go here
};
var prime = function() {
// implementation details go here
};
var ready = function(nextPromises) {
// implementation details go here
};
var service = {
getAvengersCast: getAvengersCast,
getAvengerCount: getAvengerCount,
getAvengers: getAvengers,
ready: ready
};
return service;
}
/**
* recommended
* Using function declarations
* and accessible members up top.
*/
function dataservice($http, $location, $q, exception, logger) {
var isPrimed = false;
var primePromise;
var service = {
getAvengersCast: getAvengersCast,
getAvengerCount: getAvengerCount,
getAvengers: getAvengers,
ready: ready
};
return service;
////////////
function getAvengers() {
// implementation details go here
}
function getAvengerCount() {
// implementation details go here
}
function getAvengersCast() {
// implementation details go here
}
function prime() {
// implementation details go here
}
function ready(nextPromises) {
// implementation details go here
}
}
strongloop.com/strongblog/npm-modules-travis-coveralls/
Any roadmap to implement John Papa's AngularJS Style Guide?
No angular.element, jQuery(), document.querySelector, document.queryAll, document.getElementBy*
https://github.com/johnpapa/angularjs-styleguide/edit/master/README.md
When a controller depends on a promise to be resolved before the controller is activated, resolve those dependencies in the $routeProvider
before the controller logic is executed. If you need to conditionally cancel a route before the controller is activated, use a route resolver.
Use a route resolve when you want to decide to cancel the route before ever transitioning to the View.
Why?: A controller may require data before it loads. That data may come from a promise via a custom factory or $http. Using a route resolve allows the promise to resolve before the controller logic executes, so it might take action based on that data from the promise.
Why?: The code executes after the route and in the controller’s activate function. The View starts to load right away. Data binding kicks in when the activate promise resolves. A “busy” animation can be shown during the view transition (via ng-view or ui-view)
Note: The code executes before the route via a promise. Rejecting the promise cancels the route. Resolve makes the new view wait for the route to resolve. A “busy” animation can be shown before the resolve and through the view transition. If you want to get to the View faster and do not require a checkpoint to decide if you can get to the View, consider the controller activate
technique instead.
/* avoid */
angular
.module('app')
.controller('Avengers', Avengers);
function Avengers(movieService) {
var vm = this;
// unresolved
vm.movies;
// resolved asynchronously
movieService.getMovies().then(function(response) {
vm.movies = response.movies;
});
}
/* better */
// route-config.js
angular
.module('app')
.config(config);
function config($routeProvider) {
$routeProvider
.when('/avengers', {
templateUrl: 'avengers.html',
controller: 'Avengers',
controllerAs: 'vm',
resolve: {
moviesPrepService: function(movieService) {
return movieService.getMovies();
}
}
});
}
// avengers.js
angular
.module('app')
.controller('Avengers', Avengers);
Avengers.$inject = ['moviesPrepService'];
function Avengers(moviesPrepService) {
var vm = this;
vm.movies = moviesPrepService.movies;
}
Note: The example below shows the route resolve points to a named function, which is easier to debug and easier to handle dependency injection.
/* even better */
// route-config.js
angular
.module('app')
.config(config);
function config($routeProvider) {
$routeProvider
.when('/avengers', {
templateUrl: 'avengers.html',
controller: 'Avengers',
controllerAs: 'vm',
resolve: {
moviesPrepService: moviesPrepService
}
});
}
function moviePrepService(movieService) {
return movieService.getMovies();
}
// avengers.js
angular
.module('app')
.controller('Avengers', Avengers);
Avengers.$inject = ['moviesPrepService'];
function Avengers(moviesPrepService) {
var vm = this;
vm.movies = moviesPrepService.movies;
}
Note: The code example's dependency on movieService
is not minification safe on its own. For details on how to make this code minification safe, see the sections on dependency injection and on minification and annotation.
https://github.com/johnpapa/angularjs-styleguide/edit/master/README.md
Use the controllerAs
syntax over the classic controller with $scope
syntax.
controllerAs
syntax uses this
inside controllers which gets bound to $scope
Why?: controllerAs
is syntactic sugar over $scope
. You can still bind to the View and still access $scope
methods.
Why?: Helps avoid the temptation of using $scope
methods inside a controller when it may otherwise be better to avoid them or move them to a factory. Consider using $scope
in a factory, or if in a controller just when needed. For example when publishing and subscribing events using $emit
, $broadcast
, or $on
consider moving these uses to a factory and invoke from the controller.
/* avoid */
function Customer($scope) {
$scope.name = {};
$scope.sendMessage = function() { };
}
/* recommended - but see next section */
function Customer() {
this.name = {};
this.sendMessage = function() { };
}
https://github.com/johnpapa/angularjs-styleguide/edit/master/README.md
Place bindable members at the top of the controller, alphabetized, and not spread through the controller code.
Why?: Placing bindable members at the top makes it easy to read and helps you instantly identify which members of the controller can be bound and used in the View.
Why?: Setting anonymous functions in-line can be easy, but when those functions are more than 1 line of code they can reduce the readability. Defining the functions below the bindable members (the functions will be hoisted) moves the implementation details down, keeps the bindable members up top, and makes it easier to read.
/* avoid */
function Sessions() {
var vm = this;
vm.gotoSession = function() {
/* ... */
};
vm.refresh = function() {
/* ... */
};
vm.search = function() {
/* ... */
};
vm.sessions = [];
vm.title = 'Sessions';
/* recommended */
function Sessions() {
var vm = this;
vm.gotoSession = gotoSession;
vm.refresh = refresh;
vm.search = search;
vm.sessions = [];
vm.title = 'Sessions';
////////////
function gotoSession() {
/* */
}
function refresh() {
/* */
}
function search() {
/* */
}
Note: If the function is a 1 liner consider keeping it right up top, as long as readability is not affected.
/* avoid */
function Sessions(data) {
var vm = this;
vm.gotoSession = gotoSession;
vm.refresh = function() {
/**
* lines
* of
* code
* affects
* readability
*/
};
vm.search = search;
vm.sessions = [];
vm.title = 'Sessions';
/* recommended */
function Sessions(dataservice) {
var vm = this;
vm.gotoSession = gotoSession;
vm.refresh = dataservice.refresh; // 1 liner is OK
vm.search = search;
vm.sessions = [];
vm.title = 'Sessions';
https://github.com/johnpapa/angularjs-styleguide/edit/master/README.md
Use function declarations to hide implementation details. Keep your bindable members up top. When you need to bind a function in a controller, point it to a function declaration that appears later in the file. This is tied directly to the section Bindable Members Up Top. For more details see this post.
Why?: Placing bindable members at the top makes it easy to read and helps you instantly identify which members of the controller can be bound and used in the View. (Same as above.)
Why?: Placing the implementation details of a function later in the file moves that complexity out of view so you can see the important stuff up top.
Why?: Function declaration are hoisted so there are no concerns over using a function before it is defined (as there would be with function expressions).
Why?: You never have to worry with function declarations that moving var a
before var b
will break your code because a
depends on b
.
Why?: Order is critical with function expressions
/**
* avoid
* Using function expressions.
*/
function Avengers(dataservice, logger) {
var vm = this;
vm.avengers = [];
vm.title = 'Avengers';
var activate = function() {
return getAvengers().then(function() {
logger.info('Activated Avengers View');
});
}
var getAvengers = function() {
return dataservice.getAvengers().then(function(data) {
vm.avengers = data;
return vm.avengers;
});
}
vm.getAvengers = getAvengers;
activate();
}
Notice that the important stuff is scattered in the preceding example. In the example below, notice that the important stuff is up top. For example, the members bound to the controller such as vm.avengers
and vm.title
. The implementation details are down below. This is just easier to read.
/*
* recommend
* Using function declarations
* and bindable members up top.
*/
function Avengers(dataservice, logger) {
var vm = this;
vm.avengers = [];
vm.getAvengers = getAvengers;
vm.title = 'Avengers';
activate();
function activate() {
return getAvengers().then(function() {
logger.info('Activated Avengers View');
});
}
function getAvengers() {
return dataservice.getAvengers().then(function(data) {
vm.avengers = data;
return vm.avengers;
});
}
}
Based on the name of the file (configurable), we should be able to lint the Karma Configuration file, in order to be sure to use the right properties
https://github.com/johnpapa/angularjs-styleguide/edit/master/README.md
When a controller must be paired with a view and either component may be re-used by other controllers or views, define controllers along with their routes.
Note: If a View is loaded via another means besides a route, then use the ng-controller="Avengers as vm"
syntax.
Why?: Pairing the controller in the route allows different routes to invoke different pairs of controllers and views. When controllers are assigned in the view using ng-controller
, that view is always associated with the same controller.
/* avoid - when using with a route and dynamic pairing is desired */
// route-config.js
angular
.module('app')
.config(config);
function config($routeProvider) {
$routeProvider
.when('/avengers', {
templateUrl: 'avengers.html'
});
}
<!-- avengers.html -->
<div ng-controller="Avengers as vm">
</div>
/* recommended */
// route-config.js
angular
.module('app')
.config(config);
function config($routeProvider) {
$routeProvider
.when('/avengers', {
templateUrl: 'avengers.html',
controller: 'Avengers',
controllerAs: 'vm'
});
}
<!-- avengers.html -->
<div>
</div>
When I specify rules in my .eslintc file, I'm seeing two results.
'ng_angularelement': 0,
This results in the rule still being run with the default option from rulesConfig in the plugin's index.js.
'ng_angularelement': 1
or
'ng_angularelement': 2
This results in the following error:
Warning: Definition for rule 'ng_definedundefined' was not found. Use --force to continue.
https://github.com/johnpapa/angularjs-styleguide/edit/master/README.md
When manipulating the DOM directly, use a directive. If alternative ways can be used such as using CSS to set styles or the animation services, Angular templating, ngShow
or ngHide
, then use those instead. For example, if the directive simply hides and shows, use ngHide/ngShow.
Why?: DOM manipulation can be difficult to test, debug, and there are often better ways (e.g. CSS, animations, templates)
https://github.com/johnpapa/angularjs-styleguide/edit/master/README.md
Expose the callable members of the service (its interface) at the top, using a technique derived from the Revealing Module Pattern.
Why?: Placing the callable members at the top makes it easy to read and helps you instantly identify which members of the service can be called and must be unit tested (and/or mocked).
Why?: This is especially helpful when the file gets longer as it helps avoid the need to scroll to see what is exposed.
Why?: Setting functions as you go can be easy, but when those functions are more than 1 line of code they can reduce the readability and cause more scrolling. Defining the callable interface via the returned service moves the implementation details down, keeps the callable interface up top, and makes it easier to read.
/* avoid */
function dataService() {
var someValue = '';
function save() {
/* */
};
function validate() {
/* */
};
return {
save: save,
someValue: someValue,
validate: validate
};
}
/* recommended */
function dataService() {
var someValue = '';
var service = {
save: save,
someValue: someValue,
validate: validate
};
return service;
////////////
function save() {
/* */
};
function validate() {
/* */
};
}
This way bindings are mirrored across the host object, primitive values cannot update alone using the revealing module pattern
We should add a rule to check the Dependency Inject syntax used in your AngularJS application. The Function or the Array syntax
https://github.com/johnpapa/angularjs-styleguide/edit/master/README.md
Use a capture variable for this
when using the controllerAs
syntax. Choose a consistent variable name such as vm
, which stands for ViewModel.
Why?: The this
keyword is contextual and when used within a function inside a controller may change its context. Capturing the context of this
avoids encountering this problem.
/* avoid */
function Customer() {
this.name = {};
this.sendMessage = function() { };
}
/* recommended */
function Customer() {
var vm = this;
vm.name = {};
vm.sendMessage = function() { };
}
Note: You can avoid any jshint warnings by placing the comment below above the line of code. However it is not needed when the function is named using UpperCasing, as this convention means it is a constructor function, which is what a controller is in Angular.
/* jshint validthis: true */
var vm = this;
Note: When creating watches in a controller using controller as
, you can watch the vm.*
member using the following syntax. (Create watches with caution as they add more load to the digest cycle.)
<input ng-model="vm.title"/>
function SomeController($scope, $log) {
var vm = this;
vm.title = 'Some Title';
$scope.$watch('vm.title', function(current, original) {
$log.info('vm.title was %s', original);
$log.info('vm.title is now %s', current);
});
}
https://github.com/johnpapa/angularjs-styleguide/edit/master/README.md
Use $inject
to manually identify your dependencies for AngularJS components.
Why?: This technique mirrors the technique used by ng-annotate
, which I recommend for automating the creation of minification safe dependencies. If ng-annotate
detects injection has already been made, it will not duplicate it.
Why?: This safeguards your dependencies from being vulnerable to minification issues when parameters may be mangled. For example, common
and dataservice
may become a
or b
and not be found by AngularJS.
Why?: Avoid creating in-line dependencies as long lists can be difficult to read in the array. Also it can be confusing that the array is a series of strings while the last item is the component's function.
/* avoid */
angular
.module('app')
.controller('Dashboard',
['$location', '$routeParams', 'common', 'dataservice',
function Dashboard($location, $routeParams, common, dataservice) {}
]);
/* avoid */
angular
.module('app')
.controller('Dashboard',
['$location', '$routeParams', 'common', 'dataservice', Dashboard]);
function Dashboard($location, $routeParams, common, dataservice) {
}
/* recommended */
angular
.module('app')
.controller('Dashboard', Dashboard);
Dashboard.$inject = ['$location', '$routeParams', 'common', 'dataservice'];
function Dashboard($location, $routeParams, common, dataservice) {
}
Note: When your function is below a return statement the $inject may be unreachable (this may happen in a directive). You can solve this by either moving the $inject above the return statement or by using the alternate array injection syntax.
Note: ng-annotate 0.10.0
introduced a feature where it moves the $inject
to where it is reachable.
// inside a directive definition
function outer() {
return {
controller: DashboardPanel,
};
DashboardPanel.$inject = ['logger']; // Unreachable
function DashboardPanel(logger) {
}
}
// inside a directive definition
function outer() {
DashboardPanel.$inject = ['logger']; // reachable
return {
controller: DashboardPanel,
};
function DashboardPanel(logger) {
}
}
and angular.isUndefined() instead of !angular.isDefined
Right now, we can check if each AngularJS component (service, controller, ...) start by a specific string. We can maybe use regexp to make it more configurable.
https://github.com/johnpapa/angularjs-styleguide/edit/master/README.md
Declare modules without a variable using the setter syntax.
Why?: With 1 component per file, there is rarely a need to introduce a variable for the module.
/* avoid */
var app = angular.module('app', [
'ngAnimate',
'ngRoute',
'app.shared',
'app.dashboard'
]);
Instead use the simple setter syntax.
/* recommended */
angular
.module('app', [
'ngAnimate',
'ngRoute',
'app.shared',
'app.dashboard'
]);
In order to follow the separation of concerns principle, we should not send Ajax request inside Controller. Everything should be implemented inside AngularJS service.
Not an issue, only an hint, if you wonder how to lint different part of code, this is my way. :)
- myproject
--- .eslintrc
--- src
------ client
--------- .eslintrc
------ server
--------- .eslintrc
myproject/.eslintrc
contains the common rules.rules:
brace-style: [2, "1tbs"]
comma-style: [2, "last"]
default-case: 2
func-style: [2, "declaration"]
...
src/client/.eslintrc
contains client side rules.env:
browser: true
globals:
angular: false
plugins:
- angular
src/server/.eslintrc
contains server side rules.env:
node: true
Usually I lint a javascript file when I save it. ESLint is installed also globally. As stated in config help page of ESLint, a globally-installed instance of ESLint can only use globally-installed ESLint plugins. A locally-installed ESLint can make sure of both locally- and globally- installed ESLint plugins.
That's I need to install also globally the plugin.
https://github.com/johnpapa/angularjs-styleguide/edit/master/README.md
When using a module, avoid using a variable and instead use chaining with the getter syntax.
Why?: This produces more readable code and avoids variable collisions or leaks.
/* avoid */
var app = angular.module('app');
app.controller('SomeController', SomeController);
function SomeController() { }
/* recommended */
angular
.module('app')
.controller('SomeController', SomeController);
function SomeController() { }
https://github.com/johnpapa/angularjs-styleguide/edit/master/README.md
When creating a directive that makes sense as a stand-alone element, allow restrict E
(custom element) and optionally restrict A
(custom attribute). Generally, if it could be its own control, E
is appropriate. General guideline is allow EA
but lean towards implementing as an element when it's stand-alone and as an attribute when it enhances its existing DOM element.
Why?: It makes sense.
Why?: While we can allow the directive to be used as a class, if the directive is truly acting as an element it makes more sense as an element or at least as an attribute.
Note: EA is the default for AngularJS 1.3 +
<!-- avoid -->
<div class="my-calendar-range"></div>
/* avoid */
angular
.module('app.widgets')
.directive('myCalendarRange', myCalendarRange);
function myCalendarRange() {
var directive = {
link: link,
templateUrl: '/template/is/located/here.html',
restrict: 'C'
};
return directive;
function link(scope, element, attrs) {
/* */
}
}
<!-- recommended -->
<my-calendar-range></my-calendar-range>
<div my-calendar-range></div>
/* recommended */
angular
.module('app.widgets')
.directive('myCalendarRange', myCalendarRange);
function myCalendarRange() {
var directive = {
link: link,
templateUrl: '/template/is/located/here.html',
restrict: 'EA'
};
return directive;
function link(scope, element, attrs) {
/* */
}
}
https://github.com/johnpapa/angularjs-styleguide/edit/master/README.md
Resolve start-up logic for a controller in an activate
function.
Why?: Placing start-up logic in a consistent place in the controller makes it easier to locate, more consistent to test, and helps avoid spreading out the activation logic across the controller.
Why?: The controller activate
makes it convenient to re-use the logic for a refresh for the controller/View, keeps the logic together, gets the user to the View faster, makes animations easy on the ng-view
or ui-view
, and feels snappier to the user.
Note: If you need to conditionally cancel the route before you start use the controller, use a route resolve instead.
/* avoid */
function Avengers(dataservice) {
var vm = this;
vm.avengers = [];
vm.title = 'Avengers';
dataservice.getAvengers().then(function(data) {
vm.avengers = data;
return vm.avengers;
});
}
/* recommended */
function Avengers(dataservice) {
var vm = this;
vm.avengers = [];
vm.title = 'Avengers';
activate();
////////////
function activate() {
return dataservice.getAvengers().then(function(data) {
vm.avengers = data;
return vm.avengers;
});
}
}
https://github.com/johnpapa/angularjs-styleguide/edit/master/README.md
Use the AngularUI Router for client-side routing.
Why?: UI Router offers all the features of the Angular router plus a few additional ones including nested routes and states.
Why?: The syntax is quite similar to the Angular router and is easy to migrate to UI Router.
Update NPM and Bower version property
Generate changelog
https://github.com/johnpapa/angularjs-styleguide
Define 1 component per file.
The following example defines the app
module and its dependencies, defines a controller, and defines a factory all in the same file.
/* avoid */
angular
.module('app', ['ngRoute'])
.controller('SomeController', SomeController)
.factory('someFactory', someFactory);
function SomeController() { }
function someFactory() { }
The same components are now separated into their own files.
/* recommended */
// app.module.js
angular
.module('app', ['ngRoute']);
/* recommended */
// someController.js
angular
.module('app')
.controller('SomeController', SomeController);
function SomeController() { }
/* recommended */
// someFactory.js
angular
.module('app')
.factory('someFactory', someFactory);
function someFactory() { }
For the Array syntax, if we have n services to load, the function should have 3 parameters.
https://github.com/johnpapa/angularjs-styleguide/edit/master/README.md
Services are instantiated with the new
keyword, use this
for public methods and variables. Since these are so similar to factories, use a factory instead for consistency.
Note: All AngularJS services are singletons. This means that there is only one instance of a given service per injector.
// service
angular
.module('app')
.service('logger', logger);
function logger() {
this.logError = function(msg) {
/* */
};
}
// factory
angular
.module('app')
.factory('logger', logger);
function logger() {
return {
logError: function(msg) {
/* */
}
};
}
https://github.com/johnpapa/angularjs-styleguide/edit/master/README.md
Handle and log all routing errors using $routeChangeError
.
Why?: Provides a consistent way to handle all routing errors.
Why?: Potentially provides a better user experience if a routing error occurs and you route them to a friendly screen with more details or recovery options.
/* recommended */
var handlingRouteChangeError = false;
function handleRoutingErrors() {
/**
* Route cancellation:
* On routing error, go to the dashboard.
* Provide an exit clause if it tries to do it twice.
*/
$rootScope.$on('$routeChangeError',
function(event, current, previous, rejection) {
if (handlingRouteChangeError) { return; }
handlingRouteChangeError = true;
var destination = (current && (current.title ||
current.name || current.loadedTemplateUrl)) ||
'unknown target';
var msg = 'Error routing to ' + destination + '. ' +
(rejection.msg || '');
/**
* Optionally log using a custom service or $log.
* (Don't forget to inject custom service)
*/
logger.warning(msg, [current]);
/**
* On routing error, go to another route/state.
*/
$location.path('/');
}
);
}
https://github.com/johnpapa/angularjs-styleguide/edit/master/README.md
Use named functions instead of passing an anonymous function in as a callback.
Why?: This produces more readable code, is much easier to debug, and reduces the amount of nested callback code.
/* avoid */
angular
.module('app')
.controller('Dashboard', function() { })
.factory('logger', function() { });
/* recommended */
// dashboard.js
angular
.module('app')
.controller('Dashboard', Dashboard);
function Dashboard() { }
// logger.js
angular
.module('app')
.factory('logger', logger);
function logger() { }
Because these objets/methods are managed by other rules
https://github.com/johnpapa/angularjs-styleguide/edit/master/README.md
Define a controller for a view, and try not to reuse the controller for other views. Instead, move reusable logic to factories and keep the controller simple and focused on its view.
Why?: Reusing controllers with several views is brittle and good end to end (e2e) test coverage is required to ensure stability across large applications.
When a controller must be paired with a view and either component may be re-used by other controllers or views, define controllers along with their routes.
Note: If a View is loaded via another means besides a route, then use the ng-controller="Avengers as vm"
syntax.
Why?: Pairing the controller in the route allows different routes to invoke different pairs of controllers and views. When controllers are assigned in the view using ng-controller
, that view is always associated with the same controller.
/* avoid - when using with a route and dynamic pairing is desired */
// route-config.js
angular
.module('app')
.config(config);
function config($routeProvider) {
$routeProvider
.when('/avengers', {
templateUrl: 'avengers.html'
});
}
<!-- avengers.html -->
<div ng-controller="Avengers as vm">
</div>
/* recommended */
// route-config.js
angular
.module('app')
.config(config);
function config($routeProvider) {
$routeProvider
.when('/avengers', {
templateUrl: 'avengers.html',
controller: 'Avengers',
controllerAs: 'vm'
});
}
<!-- avengers.html -->
<div>
</div>
https://github.com/johnpapa/angularjs-styleguide/blob/master/README.md
Create one directive per file. Name the file for the directive.
Why?: It is easy to mash all the directives in one file, but difficult to then break those out so some are shared across apps, some across modules, some just for one module.
Why?: One directive per file is easy to maintain.
/* avoid */
/* directives.js */
angular
.module('app.widgets')
/* order directive that is specific to the order module */
.directive('orderCalendarRange', orderCalendarRange)
/* sales directive that can be used anywhere across the sales app */
.directive('salesCustomerInfo', salesCustomerInfo)
/* spinner directive that can be used anywhere across apps */
.directive('sharedSpinner', sharedSpinner);
function orderCalendarRange() {
/* implementation details */
}
function salesCustomerInfo() {
/* implementation details */
}
function sharedSpinner() {
/* implementation details */
}
/* recommended */
/* calendarRange.directive.js */
/**
* @desc order directive that is specific to the order module at a company named Acme
* @example <div acme-order-calendar-range></div>
*/
angular
.module('sales.order')
.directive('acmeOrderCalendarRange', orderCalendarRange);
function orderCalendarRange() {
/* implementation details */
}
/* recommended */
/* customerInfo.directive.js */
/**
* @desc spinner directive that can be used anywhere across the sales app at a company named Acme
* @example <div acme-sales-customer-info></div>
*/
angular
.module('sales.widgets')
.directive('acmeSalesCustomerInfo', salesCustomerInfo);
function salesCustomerInfo() {
/* implementation details */
}
/* recommended */
/* spinner.directive.js */
/**
* @desc spinner directive that can be used anywhere across apps at a company named Acme
* @example <div acme-shared-spinner></div>
*/
angular
.module('shared.widgets')
.directive('acmeSharedSpinner', sharedSpinner);
function sharedSpinner() {
/* implementation details */
}
Note: There are many naming options for directives, especially since they can be used in narrow or wide scopes. Choose one that makes the directive and its file name distinct and clear. Some examples are below, but see the naming section for more recommendations.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.