Scott Moss @scotups
Github @Hendrixer
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!
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
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>
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();
})
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
});
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);
};
});
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!
})
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
});
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
});
};
});
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>
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.
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'
]);
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
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 />'
});
});
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
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
Dont's