Search code examples
javascriptbackbone.js

How does this Backbone todo app render all the elements to the page?


When I type in open <this_filename.html> in my terminal, it opens the page with the todolist model list already rendered on the page. How?

Here is the code:

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  <title>To-do App in Backbone.js</title>

  <!-- ========= -->
  <!--    CSS    -->
  <!-- ========= -->
  <style type="text/css">
    /* Hides bullet points from todo list */
    #todoapp ul {
      list-style-type: none;
    }
  </style>
</head>
<body>
  <!-- ========= -->
  <!-- Your HTML -->
  <!-- ========= -->

  <section id="todoapp">
    <header id="header">
      <h1>Todos</h1>
      <input id="new-todo" placeholder="What needs to be done?" autofocus>
    </header>
    <section id="main">
      <ul id="todo-list"></ul>
    </section>
  </section>
  <div>
    <p>Find the tutorial and code in <a href="http://adrianmejia.com/blog/2012/09/11/backbone-dot-js-for-absolute-beginners-getting-started/">here</a></p>
  </div>

  <!-- Templates -->
  <script type="text/template" id="item-template">
    <div class="view">
      <input class="toggle" type="checkbox">
      <label><%- title %></label>
    </div>
  </script>

  <!-- ========= -->
  <!-- Libraries -->
  <!-- ========= -->
  <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js" type="text/javascript"></script>
  <script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.3.3/underscore-min.js" type="text/javascript"></script>
  <script src="http://cdnjs.cloudflare.com/ajax/libs/backbone.js/0.9.2/backbone-min.js" type="text/javascript"></script>
  <script src="http://cdnjs.cloudflare.com/ajax/libs/backbone-localstorage.js/1.0/backbone.localStorage-min.js" type="text/javascript"></script>

  <!-- =============== -->
  <!-- Javascript code -->
  <!-- =============== -->
  <script type="text/javascript">
    'use strict';

    var app = {}; // create namespace for our app

    //--------------
    // Models
    //--------------
    app.Todo = Backbone.Model.extend({
      defaults: {
        title: '',
        completed: false
      }
    });

    //--------------
    // Collections
    //--------------
    app.TodoList = Backbone.Collection.extend({
      model: app.Todo,
      localStorage: new Store("backbone-todo")
    });

    // instance of the Collection
    app.todoList = new app.TodoList();

    //--------------
    // Views
    //--------------

    // renders individual todo items list (li)
    app.TodoView = Backbone.View.extend({
      tagName: 'li',
      template: _.template($('#item-template').html()),
      render: function(){
        this.$el.html(this.template(this.model.toJSON()));
        return this; // enable chained calls
      }
    });

    // renders the full list of todo items calling TodoView for each one.
    app.AppView = Backbone.View.extend({
      el: '#todoapp',
      initialize: function () {
        this.input = this.$('#new-todo');
        app.todoList.on('add', this.addAll, this);
        app.todoList.on('reset', this.addAll, this);
        app.todoList.fetch(); // Loads list from local storage
      },
      events: {
        'keypress #new-todo': 'createTodoOnEnter'
      },
      createTodoOnEnter: function(e){
        if ( e.which !== 13 || !this.input.val().trim() ) { // ENTER_KEY = 13
          return;
        }
        app.todoList.create(this.newAttributes());
        this.input.val(''); // clean input box
      },
      addOne: function(todo){
        var view = new app.TodoView({model: todo});
        $('#todo-list').append(view.render().el);
      },
      addAll: function(){
        this.$('#todo-list').html(''); // clean the todo list
        app.todoList.each(this.addOne, this);
      },
      newAttributes: function(){
        return {
          title: this.input.val().trim(),
          completed: false
        }
      }
    });

    //--------------
    // Initializers
    //--------------

    app.appView = new app.AppView();

  </script>

</body>
</html>

At the bottom of the code, this line: app.appView = new app.AppView(); instantiates the AppView. Is the reset function automatically called?

I think I understand how the addAll and addOne methods are defined, but I'm wondering what calls them in the first place? How is the todoList rendered on the page?


Solution

  • new app.AppView();
    

    This calls the initialize function of app.AppView.

    initialize: function () {
        // this is where addAll is called
        app.todoList.on('add', this.addAll, this);
        app.todoList.on('reset', this.addAll, this);
        app.todoList.fetch(); // Loads list from local storage
    },
    

    Listeners are bound to the add and reset events of the collection app.todoList and uses this.addAll as a callback.

    When calling .fetch() on the collection, it adds new models to it, triggering the add event, which then triggers the callback this.addAll.

    In my opinion though, with the newest Backbone version, it should use listenTo which is a better version of on:

    initialize: function () {
        this.listenTo(app.todoList, {
            'add': this.addOne, // on the add event, only use addOne
            'reset': this.addAll // on collection reset, re-render everything
        });
        app.todoList.fetch(); // Loads list from local storage
    },
    

    listenTo vs on