How to Angular
The do's and the please do not's
Scott Moss @scotups
Github @Hendrixer
Controllers
Controllers
Do's
-
set up initial state for the $scope object (or the controller itself if using the controllerAs syntax)
-
add behavior for $scope object (same as above w/ controllerAs)
-
that's it!
Please do not
-
business logic (authentication, server calls, etc)
-
DOM manipulation!!!
-
formating any input (use form controls instead)
-
filtering (use an angular filter or make one)
-
put everything on the $scope
-
store data
Controllers
angular.module('app', [])
.controller('MainController', function($scope, $http){
$scope.name = 'Main'; // the only valid thing here
$scope.numbers = [1..10000000]; // no! don't store data.
$scope.doSomething = function(){
// this function is never used in the view! no!
};
$scope.dumpMyEntireDB = function(){
return $http.get('/everything'); // don't this is ever, especially in a controller!
};
$scope.filterBy = function(testFn){
// create a filter and use $filter instead or in the view!
$scope.numbers = [].filter.call($scope.numbers, testFn);
};
});
<body ng-controller="MainController">
<!-- The only model on the scope that is used in the view -->
<h1>{{ name }}</h1>
</body>
Services, Factories
Services, Factories
Do's
-
store data
angular.module('app', [])
.factory('Todo', function($http, RESTUrl){
var TodoFactory = {
todos: [],
getTodos: function(){
return $http.get(RESTUrl + '/todos')
.then(function(resp) {
TodoFactory.todos = resp.data;
})
.catch(function(error){
// handle error
});
}
};
return TodoFactory;
})
.controller('TodoController', function($scope, Todo){
$scope.todos = Todo.todos;
Todo.getTodos();
})
Services, Factories
Do's
-
Business logic
angular.module('app', [])
.service('Auth', function($http, RESTUrl, $state){
this.login = function(credentials){
return $http({
method: 'POST',
url: RESTUrl + '/login',
data: credentials
})
.then(function(resp){
$state.go('profile', resp.data);
})
.catch(function(error){
// handles error
})
};
})
.controller('AuthController', function($scope, Auth){
$scope.login = Auth.login; // used on ng-click
});
Services, Factories
Do's
-
Share state
angular.module('app', [])
.factory('People', function(){
var people = {
all: []
};
return people;
})
.controller('PeopleController', function($scope, People){
$scope.people = People.all; // ng-repeat
$scope.addPerson = function(name){ // ng-click
$scope.people.unshift(name);
};
})
.controller('OtherController', function($scope, People){
$scope.people = People.all; // ng-repeat
$scope.removePerson = function(name){ // ng-click
$scope.people.splice($scope.people.indexOf(name), 1);
};
});
Services, Factories
Please dont's
-
try to use $scope
angular.module('app', [])
.factory('Todo', function($http, RESTUrl){
var todoFactory: {
addTodo: function(scope){
$http.post(RESTUrl, {todo: scope.todo});
}
};
return todoFactory;
})
.controller('MainController', function($scope, Todo){
Todo.addTodo($scope); // no!!
Todo.addTodo($scope.todo); // yes!
})
Services, Factories
Please dont's
-
try any DOM related stuff
angular.module('app', [])
.factory('Todo', function(){
var todoFactory: {
selectAllTodos: function(){
return $$('.todo'); // no, never
}
};
return todoFactory;
})
.controller('TodoController', function($scope, Todo){
$scope.todos = Todo.selectAllTodos(); // please don't
});
Directives
Directives
Do's
-
any and all DOM things
angular.module('app', [])
.directive('blink', function(){
return function(scope, element){
element.on('mouseenter', function(){
element.css('opacity', '0.5');
});
element.on('mouseleave', function(){
element.css('opacity', '1');
});
scope.$on('$destroy', function(){
element.off('mouseleave', 'mouseenter'); // clean up please
});
};
});
Directives
Do's
-
create custom elements, components, widgets
angular.module('app', [])
.directive('userCard', function(){
var directiveObject = {
scope: { user: '=' },
replace: true,
transclude: true,
templateUrl: 'app/user/templates/user-card.html',
link: function(scope, element, attr) {
}
};
return directiveObject;
});
<!-- app/user/templates/user-card.html -->
<div class="card">
<h1>{{ user.name }}</h1>
<img ng-src="{{ user.pofileImgUrl }}">
<p>{{ user.bio }}</p>
</div>
<!-- app/user/templates/main.html -->
<section class="container'>
<user-card
user="user"
ng-repeat="user in users">
</user-card>
</section>
Directives
Please dont's
-
never try to use selectors
angular.module('app', [])
.directive('blink', function(){
return function(scope, element){
element.on('mouseenter', function(){
$('.button').addClass('blue'); // no!
});
};
});
There is a reason why most methods on angular.element.find() don't support selectors. That is the jQuery way, instead, extend your HTML and make another Directive, its good for the soul.
Organization
Organization
Do's
-
sub modules
// app.js
angular.module('app', [
'ui.router',
'ngFx',
'app.auth',
'app.main',
'app.services'
]);
// app/main.js
angular.module('app.main', [
'app.main.directives',
'app.main.profile',
'app.main.someFeature'
]);
Organization
Do's
-
module directory structure
public/
app/
about/
about.js
about.tpl.html
home/
home.js
home.styl
home.spec.js
home.tpl.html
app.js
app.spec.js
assets/
lib/
sylus/
index.html
Organization
Do's
sub routing
// app/main/profile/profile.js
angular.module('app.main.profile', [
])
.config(function($stateProvider){
$stateProvider
.state('app.main.profile', {
url: '/profile',
controller: 'ProfileController as profile',
templateUrl: 'app/main/profile/profile.html'
});
});
// app/main/main.js
angular.module('app.main', [
'app.main.profile'
])
.config(function($stateProvider){
$stateProvider
.state('app.main', {
abstract: true,
url: '/main',
template: '<ui-view />'
});
});
Organization
Dont's
-
throw everything in one file on one module
-
inject dependencies more than once
-
have fat controllers
-
if it feels sloppy, then it is
Randoms
Randoms
Do's
only use $broadcast, $emit, and $on for atomic events
Extend your HTML
Use angular equivalents of familiar functions (ex: angular.forEach, angular.toJson)
Use angular provided sevices insted of native ones or 3rd (ex: $http not $.ajax, $window not window, $setTimeout not setTimeOut, etc)
Test it all
Extend directives with directive controllers
Bower is a standard, use it
Randoms
Dont's
- do not change anything prefixed with $ or $$
- do not namespace your services with $
- do not use ng-controller if you declared a controller in routing
- do not abuse HTML (use templates please)
- do not try to do a 'safe $apply' by checking $scope.$$phase, you are way too low in the call stack, use apply closer to the async event binding
Fin.
Be free and be Angular
How to angular
By Scott Moss
How to angular
The do's and please do not's of angular
- 1,589