ES6 & AngularJs

ES6 Main features

  • OO approach based on classes

ES6 Main features

  • OO approach based on classes

ES6 Main features

  • Module system
  • OO approach based on classes

ES6 Main features

  • Module system
  • let & const hoisting
  • OO approach based on classes

ES6 Main features

  • Module system
  • let & const hoisting
  • Native promises
  • OO approach based on classes

ES6 Main features

  • Module system
  • let & const hoisting
  • Native promises
  • Map & Set
  • OO approach based on classes

ES6 Main features

  • Module system
  • let & const hoisting
  • Native promises
  • Map & Set
  • Tail call optimizations

ES6 Syntactic sugar

  • Arrow functions

ES6 Syntactic sugar

  • Arrow functions

ES6 Syntactic sugar

  • String interpolation
  • Arrow functions

ES6 Syntactic sugar

  • String interpolation
  • Destructuring
  • Arrow functions

ES6 Syntactic sugar

  • String interpolation
  • Destructuring
  • Default parameter values 
  • Arrow functions

ES6 Syntactic sugar

  • String interpolation
  • Destructuring
  • Default parameter values 
  • Spread operator
  • Arrow functions

ES6 Syntactic sugar

  • String interpolation
  • Destructuring
  • Default parameter values 
  • Spread operator
  • And much more...

Are browsers prepared?

Do we have a solution?

Nobody said it was easy!

Let's see an example

TODO Controller


class TodoController {
  /* @ngInject */
  constructor(todoService, log) {
    this.todoService = todoService;
    this.log = log;
    this.title = 'todos';
    this.helpText = 'What needs to be done?';
    this.newTodo = {};
  }

  loadTodos() {
    return this.todoService.loadTodos().catch(() => this.log.error('Error loading todos'));
  }

  add() {
    return this.todoService.add(this.newTodo).then(() => this._resetNewTodo());
  }

  toggleCompleted(todo) {
    return this.todoService.toggleCompleted(todo);
  }
  
  get todos() {
    return this.todoService.todos;
  }

  _resetNewTodo() {
    this.newTodo = {};
  }
}

export default TodoController;

TODO Service




import _ from 'lodash';
import Todo from '~/src/app/model/todo';

const TODO = 'todo';

class TodoService {
  /* @ngInject */
  constructor(restService) {
    this.restService = restService;
    this.todos = [];
  }

  loadTodos() {
    return this.restService.list(TODO).then((todos) => this._setTodos(todos));
  }

  add(newTodo) {
    const todo = new Todo(newTodo);
    this.todos = _.union(this.todos, [todo]);
    return this.restService.add(TODO, todo);
  }

  _setTodos(todos) {
    this.todos = _.map(todos, (todo) => new Todo(todo));
  }
}

export default TodoService;

TODO Domain model









class Todo {  
  constructor(args) {
    this.id = args.id;
    this.content = args.content;
    this.completed = args.completed;
  }

  toggleCompleted() {
    return new Todo({
      id: this.id,
      content: this.content,
      completed: !this.completed
    });
  }
}

export default Todo;  

Unit tests

import TodoService from '~/src/app/components/todo-service';  
import Todo from '~/src/app/model/todo';

describe('Todo service', () => {
  let todoService, restService, rootScope, q;
  beforeEach(angular.mock.module('todo-app'));

  beforeEach(angular.mock.inject(($injector, $rootScope, $q) => {
    restService = $injector.get('restService');
    todoService = new TodoService(restService);
    rootScope = $rootScope;
    q = $q;
  }));

  it('loads the list of todos', () => {
    given:
    const todo1 = { id: 1, content: 'Buy milk', completed: true };
    const todo2 = { id: 2, content: 'Buy beer', completed: true };
    const response = q.when([todo1, todo2]);
    const loadTodosAction = sinon.stub(restService, 'list').returns(response);

    when:
    todoService.loadTodos();

    then:
    chai.expect(loadTodosAction).to.have.been.calledWith('todo');

    and:
    rootScope.$apply();

    then:
    chai.expect(todoService.todos).to.be.deep.equal([new Todo(todo1), new Todo(todo2)]);
  });
});

Integration tests




import _ from 'lodash';  
import fixtures from '~/src/fixtures/fixtures';  
import httpMock from '~/spec/integration/http-mock';

import Todo from '~/src/app/model/todo';

describe('Todo app controller', () => {  
  let controller;
  const todos = _.map(fixtures.todos, (todo) => new Todo(todo));

  beforeEach(angular.mock.module('todo-app'));
  beforeEach(httpMock.init());
  afterEach(httpMock.reset());

  beforeEach(angular.mock.inject(($controller) => {
    controller = $controller('TodoController');
  }));

  it('deletes a todo', (done) => {
    controller.loadTodos()
      .then(() => controller.delete(_.first(controller.todos)))
      .then(() => {
        chai.expect(controller.todos).to.be.deep.equal(_.tail(todos));
        done();
      }
    );
  });
});

Browser tests



class MainPage {  
  constructor() {
    this.title = element(by.css('h1'));
    this.todos = element.all(by.repeater('todo in todoController.todos'));
  }
}

export default MainPage;  


import Main from '~/spec/e2e/main.po';

describe('The main view', () => {

  const page = new Main();
  browser.get('/index.html');

  it('displays the list of todos', () => {
    expect(page.title.getText()).toBe('todos');
    expect(page.todos.count()).toBe(2);
  });
});

Coverage test report

Conclusions?

http://www.kenfos.co.uk/tdd-es6-angularjs-part-i-of-ii/

References

http://www.kenfos.co.uk/tdd-es6-angularjs-part-ii-of-ii/

http://www.github.com/doktor500

QA?

Made with Slides.com