2

So I have been working with basic Backbone.js to create a simple demo page that is a basic User Manager. I run the same function whether I am creating (POST) or updating (PUT) a user's information. Both the POST and PUT requests are successful (I.E. calling my "success" function) however only the PUT command will trigger the router, the POST request does not trigger the same line of code while it DOES trigger the success function.

Below is my events code. The saveUser function takes care of both the PUT and POST requests but the router in success is only fired from a successful PUT request, not from a successful POST request (even though both are successful in updating the database).

events: {
            'submit .edit-user-form': 'saveUser',
            'click .delete': 'deleteUser'
        },
        saveUser: function (ev) {
            var userDetails = $(ev.currentTarget).serializeObject();
            var user = new User();
            reply = user.save(userDetails, {
                success: function (){
                    router.navigate('', {trigger: true});
                },
                error: function(){
                    $("#editError").toggle();
                    $("#editError").html("Error:<br/>"+reply.status+" : "+reply.statusText+"<hr/>");
                }
            });
            return false;
        },
        deleteUser: function (ev){
            $.ajaxSetup({
                headers: {
                    'method':"DeleteUser"
                }
            });
            reply = this.user.destroy({
                success: function (){
                    router.navigate('', {trigger: true});
                },
                error: function(){
                    $("#editError").toggle();
                    $("#editError").html("Error:<br/>"+reply.status+" : "+reply.statusText+"<hr/>");
                }
            })
            return false;
        }

Here is the router code:

    var Router = Backbone.Router.extend({
        routes:{
            'new': 'editUser',
            'edit/:id': 'editUser',
            '': 'home'
        }
    });
    var router = new Router();
    //ROUTES
    router.on('route:home', function(){
        alert("1");
        userList.render(),
        editUser.render({});
    });
    router.on('route:editUser', function(id){
        userList.render();
        editUser.render({id: id});
    });
    //NECESSARY
    Backbone.history.start();`

Any help would be appreciated!

Thanks!

Patrick Lee Scott
  • 8,217
  • 3
  • 36
  • 42
MelArlo
  • 157
  • 9
  • Have you tried doing a console.log(reply) on your saveUser function and see what it returns when you do a POST? – Dennis Rongo Jan 15 '13 at 20:28
  • "The POST request does not trigger the same line of code while it DOES trigger the success function." I'm confused. So if added `console.log("Hi!");` in the `success` callback, would you get "Hi!" on both POST and PUT? – Lukas Jan 15 '13 at 20:29
  • I have tried console logging and Lukas is correct. It logs in the console as it is successful but it doesn't actually trigger the router. :/ – MelArlo Jan 15 '13 at 20:30
  • But `router.navigate` is being called in both instances? – Lukas Jan 15 '13 at 20:31
  • 1
    Is the URL of the page different when you're editing vs. creating users? – Lukas Jan 15 '13 at 20:32
  • Lukas, no. The rounter.navigate is only being called on successful PUT requests. Successful POST requests are logging in the console but not activating the router.navigate. – MelArlo Jan 15 '13 at 20:33
  • The url is the same, except the PUT request has an ID on the end. So a POST for new users would go to "../api/users/" while a PUT request would go to "../api/users/:id" – MelArlo Jan 15 '13 at 20:33
  • Sorry, I didn't mean the URL of the API request, but the URL of the page. – Lukas Jan 15 '13 at 20:35
  • Including some router code might help, if you are hitting the success callback, I think it might be a problem with router.navigate('', {trigger: true}); not hitting the callback – Patrick Lee Scott Jan 15 '13 at 20:38
  • Yes, same URL. I have put the router code in the original question up top. – MelArlo Jan 15 '13 at 20:38
  • so, what's supposed to be accomplished by router.navigate('', {trigger: true});? – Patrick Lee Scott Jan 15 '13 at 20:41
  • 1
    Basically the views it triggers should refresh the userList view and the editUser view, resetting the page and updating the information. Here is a link to the project, feel free to mess with it. http://gocella.com/backbone/ – MelArlo Jan 15 '13 at 20:43

2 Answers2

3

The problem is that when you're creating a new user, you're already at the url "", and router.navigate(newUrl) only triggers if newUrl is different than the current page url.

See https://github.com/documentcloud/backbone/issues/652.

To fix, change

router.navigate('', {trigger: true});

To

Backbone.history.fragment = null; // Forces the current URL to be dirty
router.navigate('', {trigger: true}); // Now, Backbone thinks the new URL is different
Lukas
  • 9,765
  • 2
  • 37
  • 45
  • @MelArlo, I fixed the answer so it should work now. Could you retry? – Lukas Jan 15 '13 at 20:54
  • @MelArlo, now you've got the reverse problem. Try creating two new users in a row. You'll get the same issue, because now you're routing to `#new`. – Lukas Jan 15 '13 at 20:55
  • you shouldn't be using the router for this problem, is the real problem – Patrick Lee Scott Jan 15 '13 at 20:58
  • @Lukas, i see what you mean. I did try your suggestion above but it did not get the correct result. Perhaps I just need to look at a basic refresh or redirect? – MelArlo Jan 15 '13 at 20:58
  • @MelArlo Hmm... Could you make the above change on your site and let me have a look? I've used this approach in my own projects and it has worked. I'd like to see what's different here. – Lukas Jan 15 '13 at 21:06
  • @Lukas, I have changed it and will leave it up that way for a bit. I have found a work around by validating the URL and, depending on what is matched, triggering another route for the moment. – MelArlo Jan 15 '13 at 21:19
  • OK, thanks! I found out what the problem was. I've updated the answer. That should REALLY work. :) – Lukas Jan 15 '13 at 21:22
  • 1
    @Lukas, that does work! Thanks a lot! Can you tell me what the Backbone.history.fragment = null; line does for future reference? – MelArlo Jan 15 '13 at 21:24
  • @Lukas, super sweet, thanks again! I would upvote but I need more rep. :D – MelArlo Jan 15 '13 at 21:26
1

When you post, the current url is "http://gocella.com/backbone/#", after you post you navigate to "http://gocella.com/backbone/#" which doesn't change the url, which won't fire the event.

Router only triggers the event when the url changes, it works from update because when you hit edit your url changes to "http://gocella.com/backbone/#/edit/1", and then the trigger changes back to "http://gocella.com/backbone/#"

I think the approach of using the router for this problem is just overall incorrect.

You should do something more along the lines of: when you hit save, create a new model or update the existing model with the form values, then use collection.create(modelFromForm) or, if you use model.save, then on the success callback, add it to the collection

Patrick Lee Scott
  • 8,217
  • 3
  • 36
  • 42
  • I see what you are saying Patrick, I will give that a shot. Thanks! – MelArlo Jan 15 '13 at 20:59
  • 1
    Instead of adding the result on success, why not just use `collection.create`? – Lukas Jan 15 '13 at 21:03
  • Yes, @Lukas, that's a convenience function that does all of what I just said in a much easier way. I didn't know about that. Do that. – Patrick Lee Scott Jan 15 '13 at 21:06
  • @Patrick & Lukas: I am going to check out collection.create for my final solution most likely. Thanks very much for the time/help guys. :) – MelArlo Jan 15 '13 at 21:22
  • My answer is more of a quick fix and a solution to your original question, but yeah, @PatrickScott is correct: This would be a better long-term solution. – Lukas Jan 15 '13 at 21:26