Home Manual Reference Source Test Demo

src/example/todo/app.controller.js

/**
 * @flow
 */
import Todo from './models/todo.model.js';
import listObjectTemplate from '../../components/list/list-object.hbs';
import todoTemplate from './components/todo.hbs';
import addInputComponent, { AddInputComponent } from './components/add-input';
import LocalStorageHelper from '../../helpers/local-storage.helper.js';

/**
 * Todo App Controller
 * @type {TodoAppController}
 */
export default class TodoAppController {
  addInput: AddInputComponent;
  list: Array<Todo>;

  /**
   * Sets up the Todo App
   * @param {string} domSelector DOM Selector for app container element
   */
  constructor() {

    this.list = [];
    try {
      this.loadList();
    } catch (exc) {
      console.error(exc, 'Error Loading List');
    }

    // This is a function that wraps a handlebars template in a DOM element, with event handler parameters to
    // assign to the html elements created
    this.addInput = addInputComponent(this.handleAddTodo.bind(this));
    // Add element to page after creating it
    (document.getElementById('add-container'): any).appendChild(this.addInput);

    this.renderList();
  }

  /**
   * Adds a New Todo Item to the list with the description provided
   * @param {string} description of new todo to add
   * @returns {void}
   */
  add(description: string) {
    this.list.push(new Todo(description));

    this.renderList();
    this.saveList();
  }

  /**
   * Removes a Todo Item from the list at a given index
   * @param  {number} index to remove todo at
   * @return {void}
   */
  remove(index: number) {
    this.list.splice(index, 1);
  }

  /**
   * Handler method for adding Todo to list and rerendering
   * @return {void}
   */
  handleAddTodo() {
    this.add(this.addInput.getValue());
    this.renderList();
    this.addInput.focusClear();
  }

  /**
   * Handler method for removing a Todo from the list and rerendering
   * @param  {number} index of the todo to remove from the list
   * @return {void}
   */
  handleRemoveTodo(index: number) {
    this.remove(index);
    this.renderList();
    this.saveList();
  }

  /**
   * Handler when todo completion toggle is checked
   * @param  {number} index index of todo to toggle in list
   * @return {void}
   */
  handleToggleTodo(index) {
    this.list[index].toggleComplete.call(this.list[index]);
    this.saveList();
  }

  /**
   * Method to Load the List from LocalStorage on App setup
   * @return {void}
   */
  loadList() {
    const saved: Array<Todo> = (LocalStorageHelper.get({ key: 'todos' }): any);

    saved.forEach((todo: Todo) => {
      this.list.push(new Todo(todo.description, todo.complete));
    });
  }

  /**
   * Method to Save the List to LocalStorage during any changes
   * @return {void}
   */
  saveList() {
    LocalStorageHelper.save({
      key: 'todos',
      val: this.list
    });
  }

  /**
   * Renders the HTML of the Todo App
   * @return {void}
   */
  renderList() {
    const listContainer: any = document.getElementById('list-container');
    listContainer.innerHTML = '';

    // Create list HTML with List Object Partial and local template file
    listContainer.innerHTML = listObjectTemplate({ items: this.list, template: 'todo-item-template' }, {
      partials: {
        'todo-item-template': todoTemplate
      }
    });

    // Add Handlers afterwards (because handlebars wont)
    listContainer.querySelectorAll('.todo-container input[type="checkbox"]').forEach((elm, index) => {
      elm.onchange = () => {
        this.handleToggleTodo(index);
      };
    });
    listContainer.querySelectorAll('.todo-container .trash-icon').forEach((elm, index) => {
      elm.onclick = () => {
        this.handleRemoveTodo(index);
      };
    });
  }
};