Best Practices:
Learn Angular the Hard Right Way
Best Practices:
Learn Angular the Hard Right Way
Learning AngularJS
- Angular can be easy
- when you understand core concepts and best practices
-
Or Angular can be complicated and difficult
- when learning from pieced-together online examples & snippets
- May not be best way or widely applicable
- Not comprehensive
- when learning from pieced-together online examples & snippets
Overview
- Intro to Core Concepts
- Best Practices with Code Samples
- Common Mistakes
- Best Practices for migrating to Angular 2
- Style Guides and Other Resources
Why Angular?
From "AngularJS - From Nutshell To Awesome" by Henry Tao
Architectural Principles
Structure
DRY
Testability
SoC - Separation of Concerns
Separating code into distinct sections, so that each section addresses a separate concern.
Makes it easier to:
- Maintain & extend our codebase
- Reuse code; Stay DRY
- Test, as each component will do one thing
- Isolate and fix bugs
Design Patterns
Architectural Pattern - MVC
From "The Art of AngularJS" by Matt Raible
MVC with Angular
From "AngularJS: Introduction and Sample Programs" by Amaury Valdes
A Simple View Controller
From "Learn Simple AngularJS Thanks to Best Practices" by Daniel Mettler
Best Practice #1: Thick Client
SoC for Server vs Client
From "Learn Simple AngularJS Thanks to Best Practices" by Daniel Mettler
Folder Structure
- Separate 3rd party libraries
- By Type?
- By Feature?
- works better for larger applications
/app /assets /scripts /tests
App/ Controllers/ Services/ Views/
App/ /core /pubs /parks /transport
Best Practice #2: LIFT
- L – Locating our code is easy
- I – Identify the code at a glance, when I look at file I should know what it contains.
- F – Flat structure as long as we can. Don’t make the file structure too deep, and between 3 and 7 files consider a new folder.
- T – (T-DRY) Try to stay DRY (do not repeat yourself).
Best Practice #3:
Single Responsibility
- Define 1 component per file.
This 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() { }
Single Responsibility
It is best practice to separate each component into its own file.
/* 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() { }
Readable Code
- Collaboration
- Maintenance
- Refactoring
Best Practice #4:
Avoid Global Variables
You run the risk of your code being overwritten by any other JavaScript added to the page after yours.
Can be hard to keep track of where and when global variables are being used
Instead...
Best Practice #5: Use Closures
-
Wrap Angular components in an Immediately Invoked Function Expression (IIFE).
- An IIFE removes variables from the global scope.
- An IIFE protects you against naming collisions and many global variables by providing variable scope for each file.
Before...
/* avoid */
// logger.js
angular
.module('app')
.factory('logger', logger);
// logger function is added as a global variable
function logger() { }
// storage.js
angular
.module('app')
.factory('storage', storage);
// storage function is added as a global variable
function storage() { }
After...
/**
* recommended
*
* no globals are left behind
*/
// logger.js
(function() {
'use strict';
angular
.module('app')
.factory('logger', logger);
function logger() { }
})();
// storage.js
(function() {
'use strict';
angular
.module('app')
.factory('storage', storage);
function storage() { }
})();
Best Practices for
Naming Conventions
- Avoid naming collisions
- Avoid generic names or keywords.
- Use unique naming conventions with separators for sub-modules.
Best Practices for
Naming Conventions
- Component names should be descriptive.
- Name controllers with Pascal casing
- Controllers start with capital letter
- Use camel case for all non-controllers
- Use suffixes to denote various entities
- Add 'Controller' suffix to all controllers and 'Factory' suffix to all factories, etc.
Best Practices for
Declaring Module Dependencies
Modules are containers of Angular components. Modules can be grouped into 3 categories:
- AngularJS modules (ngRoute, ngAnimate)
- 3rd party modules (kendo, breeze etc.) – modules that someone else wrote
- Custom modules - our modules
Best Practices for
Declaring Module Dependencies
- Define dependencies for each module in each module
- Define dependencies at specific levels
Best Practices for
Function Naming
- Anonymous Functions
- used in one place only
- smaller functions
- only if easily readable
- Named Functions
- used in larger, more complex functions
- easier to read and reuse
Best Practice: Avoid Complex Expressions In HTML
- Reduces readability
- Harder to maintain
(JS Fiddle Demo)
Patterns
For Registering, Injecting and Defining Components
Register & Inject, Then Function
- Array can become long and hard to read
angular
.module('app.pubs', [])
.controller('Pubs',['core’,’dataservice',Pubs]);
function Pubs(core, dataservice) {
//controller goes here
}
Function, Inject, Register
- $inject keeps the injection separate
- avoid ugly array of injectables
- scroll to see the injections
- possible issues with dependency sequence
function Pubs(core, dataservice) {
//controller goes here
}
Pubs.$inject = ['core’,’dataservice']);
angular
.module('app.pubs', [])
.controller('Pubs',Pubs]);
Register, Inject, Function
- can see the injection list right next to the controller’s Parameters
angular
.module('app.pubs', [])
.controller('Pubs',Pubs]);
Pubs.$inject = ['core’,’dataservice'];
function Pubs(core, dataservice) {
//controller goes here
}
Inject Later with ngAnnotate
- use with automation tools like Gulp
- injects the dependencies just by adding the @ngInject Annotation
angular
.module('app.pubs', [])
.controller('Pubs',Pubs]);
/* @ngInject */
function Pubs(dataservice) {
//controller goes here
}
Common Mistakes
- Too Many Watchers
- noticeable performance issue at around 2000 watchers
- Use bind once
- Using jQuery
- DOM manipulations via directives
Angular 1 to 2 Best Practices
Avoid:
- $scope
- $observe
- $parse
- $eval
- link function
Use:
- Isolated Scope
- ControllerAs
- BindToController
- ES6 Classes
- new 1.4 router
Style Guides
- John Papa's: https://github.com/johnpapa/angularjs-styleguide
- Todd Motto's: https://github.com/toddmotto/angularjs-styleguide
- Minko Gechev's: https://github.com/mgechev/angularjs-style-guide
Sources
- http://static.raibledesigns.com/repository/presentations/The_Art_of_AngularJS_DeRailed2014.pdf
- http://dnn-connect.org/community/blogs/dnn-connect-2015-video-learn-simple-angularjs-thanks-to-best-practices
- Bullet Three
- http://www.effectiveui.com/blog/2015/04/20/learned-ng-conf-write-angularjs-migration-mind/
- http://blog.zuehlke.com/en/angularjs-clean-code/
- http://www.codeproject.com/Articles/869712/AngularJS
- http://www.nonlinearcreations.com/Digital/how-we-think/articles/2015/03/AngularJS-Best-Practices.aspx
AngularJS Best Practices
By Erica Stanley
AngularJS Best Practices
Angular & Ionic Workshop, 2015
- 3,144