1

I am new to Javascript and backbone.js, so hopefully I am missing something simple here. I am experimenting with some sample code I found which is supposed to check for unsaved changes before allowing a user to navigate away to another page. I have created a JSfiddle here:

http://jsfiddle.net/U43T5/4/

The code subscribes to the hashchange event like this:

$(window).on("hashchange", router.hashChange);

And the router.hashChange function checks a "dirty" flag to determine whether or not to allow the navigation, like this:

hashChange: function (evt) {
    if (this.cancelNavigate) { // cancel out if just reverting the URL
        evt.stopImmediatePropagation();
        this.cancelNavigate = false;
        return;
    }
    if (this.view && this.view.dirty) {
        var dialog = confirm("You have unsaved changes. To stay on the page, press cancel. To discard changes and leave the page, press OK");
        if (dialog == true) return;
        else {
            evt.stopImmediatePropagation();
            this.cancelNavigate = true;
            window.location.href = evt.originalEvent.oldURL;
        }
    }
},

The problem is that the code is not working because this.view is undefined, so the 2nd if block is never entered.

I would like the sample program to always ask for confirmation before navigating away from the page (in my sample program, I have set this.view.dirty to always be true, which is why it should always ask for confirmation). Or if there is a better approach, I am open to alternatives.

JohnD
  • 14,327
  • 4
  • 40
  • 53

2 Answers2

1

The main issue is the this context in the methods , this corresponds to the Window Object and not the Router. So it always remains undefined as you are defining view on the router. Declare a initialize method which binds the context inside the methods to router.

 initialize: function() {
        _.bindAll(this, 'loadView', 'hashChange');
    },

Check Fiddle

Sushanth --
  • 55,259
  • 9
  • 66
  • 105
  • Your fiddle is the same as the OP's – fbynite Jul 26 '13 at 17:51
  • @fbynite.. Thanks for pointing it out.. Posted the updated fiddle – Sushanth -- Jul 26 '13 at 17:53
  • Thanks for you answer (upvoted). I was wondering why you suggest changing the order of "history.start()" to put it before the event bindings? When I run that code, it seems to render the new view and then if I click cancel, it re-renders the original view (not really desired behavior). If I put back the original order (put "history.start()" at the end), it doesn't render the new view unless I click ok. For my education, can you explain why I want change #1? – JohnD Jul 26 '13 at 18:16
  • I think I misunderstood your question regarding #1. I wrote that up thinking of something else. The main issue was the context to which this was bound to . i will update the post – Sushanth -- Jul 26 '13 at 18:32
  • 3
    @Sushanth-- Fiddle not found. – Coding man Jul 22 '15 at 09:18
0

I spent a lot of time to make at least something decent.

I ended up writing a wrapper for Backbone function:

    var ignore = false;

    Backbone.history.checkUrl = function() {

            if (ignore) {
                ignore = false;
                return;
            }

            app.dirtyModelHandler.confirm(this, function () {
                Backbone.History.prototype.checkUrl.call(Backbone.history);
            },
            function() {
                ignore = true;
                window.history.forward();

            });

    };

app.dirtyModelHandler.confirm is a function which shows confirmation (Ok, Cancel) view and takes success and cancel functions as arguments.

user2846569
  • 2,752
  • 2
  • 23
  • 24