javascriptbackbone.jsbackbone-viewsbackbone-eventsbackbone-routing

Backbone.js: Why isn't event firing the second time?


I'm relatively new to Javascript and backbone. I'm hacking on a simple, beginner-level backbone app. I have two views: Home and NewEntry. From Home, I click on "new entry" to get to render NewEntry. From NewEntry, I click on "cancel" to render Home. So, I'd like to toggle between Home and NewEntry by clicking on "cancel" and "new entry" respectively.

However, it doesn't work. This is what I try to do:

  1. On Home, click on "new entry." NewEntry renders properly.
  2. On NewEntry, click on "cancel." Home renders properly.
  3. On Home, click on "new entry." NewEntry renders properly again.
  4. On NewEntry, click on "cancel." Home does NOT render. The method associated with the click event does NOT fire a second time.

I'm extremely stumped! I can't figure out why. I'd like to figure out if a different event is firing, but I don't know how. Does anybody know what I should do?

JavaScript:

var Views = {
    Home: Backbone.View.extend({
        template: $('#homeview-template').template(),
        initialize: function() {
            this.entries = new Collections.Entries();
            this.entries.on('all', this.render, this);
            this.entries.fetch();
        },
        render: function() {
            var currDate = getCurrentDate();
            var innerHtml = $.tmpl(this.template, {
                currDate: currDate
            });
            this.$el.html(innerHtml);
            return this;
        }
    }),

    NewEntry: Backbone.View.extend({
        template: $('#newentryview-template').template(),
        events: {
            'click button#cancel-new-entry': 'cancelNewEntry',
            'all': 'logEvents'
        },
        initialize: function() {
            // currently, do nothing
        },
        render: function() {
            var innerHtml = $.tmpl(this.template);
            this.$el.html(innerHtml)
            return this;
        },
        cancelNewEntry: function() {
            console.log("test 1");
            window.router.showHome();
        },
        logEvents: function(evnt) {
            console.log("works");
            console.log("event", evnt);
        }
    })
};

var Router = Backbone.Router.extend({
    initialize: function(inputs) {
        this.homeView = new Views.Home();
        this.newEntryView = new Views.NewEntry();
        this.el = inputs.el
    },

    routes: {
        "": 'showHome',
        "#": 'showHome',
        'new': 'writeNewEntry'
    },
    showHome: function() {
        this.el.empty();
        this.navigate("");
        this.el.append(this.homeView.render().el);
    },
    writeNewEntry: function() {
        this.el.empty();
        this.navigate('new');
        this.el.append(this.newEntryView.render().el);
    }
});

HTML:


<script id='homeview-template' type='text/template'>
<h1 class='title'>My Journal</h1>
<div id='curr-date-new-entry'>
<div id='curr-date'>${currDate}</div>
<div id='new-entry'>
<a href='#/new'>New Entry</a>
</div>
</div>
</script>
<script id='newentryview-template' type='text/template'>
<h1 class='title'>New Entry</h1>
<input id='new-entry-title' placeholder='New entry title' type='text'/>
<input id='new-entry-body' placeholder='New entry content' type='text'/>
<button id='submit-new-entry'>Submit</button>
<button id='cancel-new-entry'>Cancel</button>
</script>


Solution

  • This probably fixes your issue:

    writeNewEntry: function() {
        this.el.empty();
        this.navigate('new');
        this.el.append(this.newEntryView.render().el);
        //fix: http://backbonejs.org/#View-delegateEvents
        this.newEntryView.delegateEvents();
    }
    

    However if your are not reusing views I will recommend you to instantiate the view directly on the method writeNewEntry instead of initialize, in this way you will not need to call manually delegateEvents:

    writeNewEntry: function() {
        this.el.empty();
        this.navigate('new');
        this.newEntryView = new Views.NewEntry();
        this.el.append(this.newEntryView.render().el);
    }