This document discusses best practices for organizing AngularJS applications. It recommends organizing files by feature rather than type, with each feature having related HTML, CSS, tests, etc. It also recommends structuring modules to mirror the URL structure and listing submodules as dependencies. The document discusses using services for reusable logic rather than large controllers. It emphasizes writing tests, managing technical debt, following code style guides, and using task runners like Grunt or Gulp to automate tasks.
3. The way it was…
Mixed client server leads to
increased complexity
Presentation logic will require a lot
of interactions with the server
Not a real application, just another
website
4. The way it should be
Client Server Data
MV* JS framework, Business
logic, Mobile-ready layout
Data storage.
Treated like a black box.
Thin-Server, REST, Event
11. BROS DON’T LET BROS
ORGANIZE FILES
BY TYPE
– the angularjs bro code
12. Organizing by type
Organize by services, controllers, views, etc.1
One module per directory.2
Directories become bloated.4
Poor control over your DI configuration.3
13. Folders-by-Feature Structure
Organize by component / feature.1
Directory structure mirrors app structure.2
Can create angular modules that more
accurately reflect injection dependencies.
4
Easier to extract / share components.3
14. Folders-by-Feature Structure
All files related to a feature live together (html,
css, tests).
Application-level files live directly under app/
Each section / page of the app has its own folder
under app/
Things used throughout the app live under
common/
Files related to the app live outside of
app/
! super-dooper-project/
|- src/
| |- app/
| | |- <app logic>
| |- assets/
| | |- <static files>
| |- common/
| | |- components/
| | |- services/
| |- less/
| | |- main.less
|- bower_components/
| |- bootstrap_components/
| |- angular-bootstrap/
| |- bootstrap/
|- karma/
|- .bowerrc
|- bower.json
|- build.config.js
|- Gruntfile.js
|- module.prefix
|- module.suffix
|- package.json
1
2
4
3
5
15. Application layout
Modules should mirror URL
Submodules live near main module
Submodules listed as dependencies to main
module
src/
|- app/
| |- user/
| | |- create/
| | |- search/
| | |- user.js
| | |- user.ctrl.js
| | |- user.less
| | |- user.spec.js
| | |- user.tpl.html
1
2
3
https://github.com/ngbp/ngbp by Josh Miller
angular.module(‘app.user', [‘app.user.create’, ‘app.user.search’]);
angular.module(‘app.user.create’, []);
17. Angular talks about…
Services
are app-‐wide
injected
singletons.
Controllers
are bridge between
template and the rest of
your application logic.
Directives
are encapsulation of
some widget or DOM
element behavior.
Filters
are simple
output formatting
functions.
20. The problem with controllers
Common functionality
List pages, properties pages, etc.
1
Very complicated views can end up having multi-thousand line
controllers.
2
How can you break up logic that is going to be used in many
controllers?
3
21. Option 1 – inheritance
What is it?
Traditional OOP inheritance
e.g. BaseListController → sorting, paging, filtering…
Cons
Hierarchy can become too deep and confusing
Some controllers in our app are 5 levels deep
Some logic doesn't fit neatly into a generic parent
Imperfect encapsulation
22. Option 2 – mixins
What is it?
Object whose properties are mixed into another.
Cons
It becomes unclear where code comes from.
Mixins could potentially conflict with or overwrite each other.
Mixin is tightly coupled to the mixed-‐into object.
!
angular.extend(ctrl, ListMixin);
23. Option 3 – object composition
What is it?
Traditional object composition that takes advantage of Angular's dependency injection.
Cons
Can’t inject the objects themselves (﴾until angular 2.0)﴿
!
ctrl.listManager = $injector.instantiate(app.ListManager, {});
Pros
Better encapsulation
Easier reuse
Forces more coherent API design
25. Technical debt
Tests will be in the next release
Code entropy: “if touch that code everything
will break”
Docs? My code is state of art!
TODO/FIXME statements
Let’s just copy/paste for now
1
2
4
3
5
Can be found in any project
Do NOT let it grow
http://en.wikipedia.org/wiki/Technical_debt
QUALITY
26. Code style
Readability
Good names
Tabs/Spaces convention
Clear logic
Docs and comments
1
2
4
3
5
!
• jshint+stylish
• plato
• code painter
• editorconfig
• jscs
• eslint
These are your friends
Tools
27. Single responsibility
File per each component1
• Gives the most control over how the injector is configured
• Much easier to extract code into shared components
• When testing, only have to load specific module under test
angular.module('app', ['ngRoute']);
angular.module(‘app’).controller('SomeController', SomeController);
!
function SomeController() { }
angular.module(‘app’).factory('someFactory', SomeFactory);
!
function SomeFactory() { }
28. IIFE
Wrap components in an Immediately Invoked Function Expression1
• An IIFE removes variables from the global scope.
• Protects us from having collisions of variables and many global variables.
(function() {
'use strict';
angular.module(‘app’)
.controller('SomeController', SomeController);
!
function SomeController() { }
})();
29. ControllerAs Controller Syntax
• Use the controllerAs syntax over the classic controller with $scope syntax.
• The controllerAs syntax uses this inside controllers which gets bound to
$scope
• controllerAs is syntactic sugar over $scope. You can still bind to the View
and still access $scope methods.
• Use a capture variable for this when using the controllerAs syntax.
(function() {
'use strict';
angular.module(‘app’)
.controller('SomeController', SomeController);
!
function SomeController() {
var ctrl = this;
ctrl.name = {};
ctrl.sendMessage = function() { };
}
})();
30. Resolve promises for your controllers
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.
(function() {
'use strict';
angular.module(‘app’).config(myConfig);
! function myConfig($routeProvider) {
$routeProvider
.when('/avengers', {
templateUrl: 'avengers.html',
controller: 'AvengersCtrl',
controllerAs: 'av',
resolve: {
resolvedMovies: function(movieService) {
return movieService.getMovies();
}
}
});
}
})();
(function() {
'use strict';
angular.module(‘app’).controller(‘AvengersCtrl’, Avengers);
! function Avengers(resolvedMovies) {
var ctrl = this;
ctrl.moviesList = resolvedMovies.data;
}
})();
Really good for testing as you can
mock injectable data
32. Test driven development
TDD/BDD
Better code understanding
Release faster
Motivation
Reliability
1
2
4
3
5
Long (﴾hours)﴿
Medium (﴾minutes)﴿
Fast (﴾seconds)﴿
UIEnd 2 End
API
Services
Database
Headless
Smoke tests
unit tests
Till first failed
Remote
Local.
Stubs+Mocks
Safe refactoring
33. Test driven development
TDD/BDD
Better code understanding
Release faster
Motivation
Reliability
1
2
4
3
5
Fast (seconds)
it('should have Avengers controller', function() {
//TODO
});
!it('should find 1 Avenger when filtered by name', function() {
//TODO
});
!it('should have 10 Avengers', function() {}
//TODO (mock data?)
});
!it('should return Avengers via XHR', function() {}
//TODO ($httpBackend?)
});
!// and so on
34. Сode coverage
Not tested area of application
Dead code detection
Acceptance threshold 70-90%
Testing quality
Reports
1
2
4
3
5
Tools
Istanbul
JSCoverage
Blanket
coverage > 80% is AWESOME
coverals.io
codeclimate
History and stats service
35. TDD with…
+
Cool for E2E
tests
I’m your test
runner.
!
The best one.
These two guys are
GOOD.
37. What tasks to automate
Source
Concatenate
Uglify
SourceMaps
Watch
LiveReload
Rebuild
Serve
Preprocess
LESS
SASS
Compass
Test
Karma
Mocha
Coverage
Assets
Templates
HTML processing
Images optimization
Custom
ChangeLog
Notifications
console.debug
38. Task runner: grunt
FILE BASED
Good for file operations like copy/move/save. Configuration is
over the code
1
TONS OF PLUGINS
Many of the tasks you need are already available as Grunt Plugins,
and new plugins are published every day.
2
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
uglify: {
options: {
banner: '/*! <%= pkg.name %>*/n'
},
build: {
src: 'src/<%= pkg.name %>.js',
dest: 'build/<%= pkg.name %>.min.js'
}
}
});
http://gruntjs.com
http://gruntjs.com/plugins
39. The streaming build system: gulp
Fast (seconds)
EASY TO USE
By preferring code over configuration, gulp keeps simple things
simple and makes complex tasks manageable.
1
STREAM BASED
Much more faster then Grunt for file-‐content processing operations
2
var gulp = require('gulp');
!gulp.task('default', function() {
// place code for your default task here
});
http://gulpjs.com
http://gulpjs.com/plugins
42. Server as platform
Customizable stack and environment
Own services to use
Infrastructure
Need for DevOps
1
2
4
3
Fast (seconds)
Amazon, Digital Ocean or
Rackspace
43. Platform as a Service
Takes care of infrastructure for you
Updating packages and installing security
patches
Technical support 24/7
Reliability and Monitoring
1
2
4
3
Azure
Heroku
Nodejitsu
CloudFoundry
44. Docker
Your own PaaS
Open Source
Ready to use stacks
Easy Scale
Easy to migrate
1
2
4
3
5
Deis
Flynn
Tsuru
Octohost
Tools
Application + Platform = Container