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

Made with Slides.com