Todo App Code

The code for the Todo app tutorial can be found below as a comparison with your own application. This code is for demonstration purposes - it is not necessarily the best way to build such applications and it certainly isn't the only way.

ES6 version of the code

Driver file

driver.js:

var Marionette = require('backbone.marionette');
var TodoView = require('./views/layout');

var initialData = {
  items: [
    {assignee: 'Scott', text: 'Write a book about Marionette'},
    {assignee: 'Andrew', text: 'Do some coding'}
  ]
};


var App = new Marionette.Application({
  onStart: function(options) {
    var todo = new TodoView({
      collection: new Backbone.Collection(options.initialData.items),
      model: new ToDoModel()
    });
    todo.render();
    todo.triggerMethod('show');
  }
});

App.start({initialData: initialData});

Views

Everything in this section is contained in the views/ directory.

layout.js:

var Backbone = require('backbone');
var Marionette = require('backbone.marionette');
var ToDoModel = require('../models/todo');

var FormView = require('./form');
var ListView = require('./list');


var Layout = Marionette.LayoutView.extend({
  el: '#app-hook',

  template: require('../templates/layout.html'),

  regions: {
    form: '.form',
    list: '.list'
  },

  collectionEvents: {
    add: 'itemAdded'
  },

  onShow: function() {
    var formView = new FormView({model: this.model});
    var listView = new ListView({collection: this.collection});

    this.showChildView('form', formView);
    this.showChildView('list', listView);
  },

  onChildviewAddTodoItem: function(child) {
    this.model.set({
      assignee: child.ui.assignee.val(),
      text: child.ui.text.val()
    }, {validate: true});

    var items = this.model.pick('assignee', 'text');
    this.collection.add(items);
  },

  itemAdded: function() {
    this.model.set({
      assignee: '',
      text: ''
    });
  }
});

module.exports = Layout;

form.js:

// views/form.js
var Marionette = require('backbone.marionette');


var FormView = Marionette.LayoutView.extend({
  tagName: 'form',
  template: require('../templates/form.html'),

  triggers: {
    submit: 'add:todo:item'
  },

  modelEvents: {
    change: 'render'
  },

  ui: {
    assignee: '#id_assignee',
    text: '#id_text'
  }
});


module.exports = FormView;

list.js:

// views/list.js
var Marionette = require('backbone.marionette');

var ToDo = Marionette.LayoutView.extend({
  tagName: 'li',
  template: require('../templates/todoitem.html')
});


var TodoList = Marionette.CollectionView.extend({
  tagName: 'ul',
  childView: ToDo
});


module.exports = TodoList;

Templates

Everything here is stored in the templates directory.

layout.html:

<div class="list"></div>
<div class="form"></div>

form.html:

<label for="id_text">Todo Text</label>
<input type="text" name="text" id="id_text" value="<%- text %>" />
<label for="id_assignee">Assign to</label>
<input type="text" name="assignee" id="id_assignee" value="<%- assignee %>"/>

<button id="btn-add">Add Item</button>

todoitem.html:

<%- item.text %> &mdash; <%- item.assignee %>

Models

Everything here is under the models directory:

todo.js

var Backbone = require('backbone');


var ToDo = Backbone.Model.extend({
  defaults: {
    assignee: '',
    text: ''
  },

  validate: function(attrs) {
    var errors = {};
    var hasError = false;
    if (!attrs.assignee) {
      errors.assignee = 'assignee must be set';
      hasError = true;
    }
    if (!attrs.text) {
      errors.text = 'text must be set';
      hasError = true;
    }

    if (hasError) {
      return errors;
    }
  }
});


module.exports = ToDo;

Todo App Code - ES6 Version

The following code will load in latest version of Google Chrome with Marionette 2.4.7 without the need to compile back to ES5. Older browsers may require compiling with Babel or similar.

Driver file

driver.js:

import Backbone from 'backbone';
import Marionette from 'backbone.marionette';

import TodoView from './views/layout';
import TodoModel from './models/todo';

const initialData = [
  {assignee: 'Scott', text: 'Write a book about Marionette'},
  {assignee: 'Andrew', text: 'Do some coding'}
];

export class TodoApp extends Marionette.Application
{
  onStart(options)
  {
    const todoView = new TodoView({
      collection: new Backbone.Collection(options.initialData),
      model: new TodoModel()
    });
    todoView.render();
  }
}

window.app = new TodoApp;
window.app.start({initialData: initialData});

Views

Everything in this section is contained in the views/ directory.

layout.js:

import Marionette from 'backbone.marionette';

import FormView from './form';
import ListView from './list';
import LayoutTemplate from '../templates/layout.html';

export default class TodoView extends Marionette.LayoutView
{
  constructor(options)
  {
    options.template = LayoutTemplate;
    options.el = '#app-hook';
    options.regions = {
      form: '.form',
      list: '.list'
    };

    super(options);
  }

  onRender()
  {
    const formView = new FormView({model: this.model});
    const listView = new ListView({collection: this.collection});
    this.showChildView('form', formView);
    this.showChildView('list', listView);
  }

  collectionEvents()
  {
    return { add: 'itemAdded' };
  }

  onChildviewAddTodoItem(child)
  {
    this.model.set({
      assignee: child.ui.assignee.val(),
      text: child.ui.text.val()
    }, {validate: true});

    if (this.model.isValid()) {
      const items = this.model.pick('assignee', 'text');
      this.collection.add(items);
    }
  }

  itemAdded()
  {
    this.model.set({
      assignee: '',
      text: ''
    });
  }
}

form.js:

import Marionette from 'backbone.marionette';
import FormTemplate from '../templates/form.html';

export default class FormView extends Marionette.LayoutView
{
  constructor(options)
  {
    options.template = FormTemplate;
    options.tagName = 'form';

    super(options);
  }

  triggers()
  {
    return {
      submit: 'add:todo:item'
    };
  }

  modelEvents()
  {
    return {
      change: 'render'
    };
  }

  ui()
  {
    return {
      assignee: '#id_assignee',
      text: '#id_text'
    };
  }
}

list.js:

import Marionette from 'backbone.marionette';
import TodoItemTemplate from '../templates/todoitem.html';

class Todo extends Marionette.LayoutView
{
  constructor(options)
  {
    options.template = TodoItemTemplate;
    options.tagName = 'li';

    super(options);
  }
}

export default class ListView extends Marionette.CollectionView
{
  constructor(options)
  {
    options.tagName = 'ul';
    options.childView = Todo;

    super(options);
  }
}

Templates

Everything here is stored in the templates directory. All template files are the same as the ES5 version.

layout.html:

<div class="list"></div>
<div class="form"></div>

form.html:

<label for="id_text">Todo Text</label>
<input type="text" name="text" id="id_text" value="<%- text %>" />
<label for="id_assignee">Assign to</label>
<input type="text" name="assignee" id="id_assignee" value="<%- assignee %>"/>

<button id="btn-add">Add Item</button>

todoitem.html:

<%- item.text %> &mdash; <%- item.assignee %>

Models

Everything here is under the models directory:

todo.js

import Backbone from 'backbone';

export default class TodoModel extends Backbone.Model
{
  defaults()
  {
    return {
      assignee: '',
      text: ''
    };
  }

  validate(attrs)
  {
    const errors = {};
    let hasError = false;
    if (!attrs.assignee)
    {
      errors.assignee = 'assignee must be set';
      hasError = true;
    }
    if (!attrs.text)
    {
      errors.text = 'text must be set';
      hasError = true;
    }
    if (hasError) return errors;
  }
}