Add the ability to disable navigate same url short circuit #1214

Closed
markdaws opened this Issue Apr 11, 2012 · 17 comments

Comments

Projects
None yet

In the navigate method of the Router object, there is the following line:

if (this.fragment == frag) return;

So if the target URL is the same as the current URL, the navigate method does nothing. In my app I want to be able to execute the routing code even if the url is the same.

I'm thinking that an extra field could be passed to the options object of the navigate method:

force: [true|false]

by default this is set to false, but if set to true the navigate method would still run even if the current url is the same as the target url. Has this been discussed before? I am willing to do a pull request with the change if it would be accepted to the project.

Thanks
Mark.

Owner

jashkenas commented Apr 11, 2012

Yep -- it's been discussed a bunch of times before. Instead of calling .navigate, simply call the function you wanted to call in the first place. In fact, the trigger: true option to navigate is often a code smell.

@jashkenas jashkenas closed this Apr 11, 2012

Here's the problem I have with this closing this bug:

A key point of using backbone is to handle parsing of routes, and then calling a function based on the route. Lacking the ability to determine cases where a "reload" is ok through backbone directly, we have to do something to this effect:

        if (url == window.location.pathname + window.location.search) {
            Backbone.history.loadUrl(url);
            return false;
        }
        else if (app.router.isHandled(url)) {
            Backbone.history.navigate(url, {
                trigger: true,
                forceReload: true
            });
            return false;
        }

Does this work? yea, but it does feel like this is a totally valid use case that would be nice to have in the platform.

Are there any articles you can point me towards on why using "trigger:true" is code smell?

thanks

Owner

jashkenas commented Aug 28, 2012

There aren't any articles ... but because it forces you to serialize your application state through a string (a URL, to be precise), when you don't need to. Just calling a JavaScript function is a much richer API ... you can pass objects, references, and you don't lose all of your external state.

Think of Backbone's changing the URL in the browser as dropping a bookmark that folks can go back to later -- not as a thing that you need to do to change your application's state.

magalhas commented Mar 6, 2014

Though I totally agree with @jashkenas reply on the top of the thread, I kind of agree more with @dotBits opinion. Backbone is great for not enforcing any patterns, as a developer it is expected that navigate + {trigger: true} will always fire any bound methods and route events, even if navigating to the same URL as before.

Ulrira commented Mar 11, 2014

Same need here.

I have a table with pagination and I'm deleting a row. After that, I want to reload the same #url to reload the current page of the table.

If the deleted row is the last, I want to reload the same url but changing the page to page-1

tyrw commented Apr 17, 2014

+1 for same need case as @Ulrira

Another 👍 to add this flexibility. I understand the points brought up with the quoted article but those are really just opinions at the end of the day. Good opinions, and things to be aware of (don't use DELETE routes), but still this opinion driving the baseline of all Backbone users is quite ridiculous.

paton commented Sep 7, 2014

+1.

I have logic in my router that determines whether a logged out user is allowed to view the requested route.

When a user logs out, I'd like to be able to reload the current route, which runs the router logic that checks if the logged out user is still allowed to view the page.

Owner

jashkenas commented Sep 23, 2014

When a user logs out, I'd like to be able to reload the current route, which runs the router logic that checks if the logged out user is still allowed to view the page.

When a user logs out, just call the function that checks if the user is still allowed to view the page.

those are really just opinions at the end of the day

Backbone has opinions. One of them is that you don't stuff useful reusable logic into an only-activated-by-a-URL callback that you can't easily call from other places. Just use a function.

paton commented Sep 23, 2014

@jashkenas I agree with you.. ideally I'd be able to call the isolated function that checks if the user is still allowed to view the page instead of recalling the entire route logic. But unfortunately that method is intertwined with other logic that makes your suggestion infeasible. Unfortunately I don't have time to refactor that part of my app to work as you suggested.

IMO allowing Backbone to rerender the current route is a fairly innocuous change that would (in my case) reduce the need for a significant refactor.

Backbone has opinions. One of them is that you don't stuff useful reusable logic into an only-activated-by-a-URL callback that you can't easily call from other places. Just use a function.

Now that I'm familiar with the inner workings of Backbone, I completely agree with this methodology. Unfortunately the "dont rely on routes to execute logic" opinion isn't expressed clearly in the Backbone docs.

Perhaps we should add a "best practices" section to the docs that outlines the ways in which Backbone is most opinionated to help developers avoid learning these "gotchas" the hard way.

Owner

jashkenas commented Sep 23, 2014

The only way documented in the Backbone docs at least has you defining the route as a method on your router object.

If you're truly unable to refactor the "allowed to view" check out into a function (something I find a bit difficult to believe) — can't you at least just call the method on the router directly?

paton commented Sep 23, 2014

@jashkenas I don't have a good idea of an alternative to using { trigger: true } to trigger a URL change and the associated route logic. I'm still confused as to why this is an anti-pattern, or code smell as you suggested.

Is this how you would do it?

Backbone.View.extend({
  onLogin: function() {
    router.navigate('welcome');
    router.showWelcomePage();
  }
});

Instead of this... the trigger: true version?

Backbone.View.extend({
  onLogin: function() {
    router.navigate('welcome', { trigger: true });
  }
});
Owner

jashkenas commented Sep 23, 2014

This is how I would do it.

router.onLogin();

paton commented Sep 23, 2014

That pattern at scale would results in an extremely convoluted router file with a ton of methods like "onLogin", "onCheckout", "onPaymentReceived", "onViewSettingsPage". This is probably why a lot of people prefer the { trigger: true } approach.

spenoir commented Sep 30, 2015

Think of Backbone's changing the URL in the browser as dropping a bookmark that folks can go back to later -- not as a thing that you need to do to change your application's state.

I would say that it is the only state machine a web developer should use.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment