emberjs / ember.js

Ember.js - A JavaScript framework for creating ambitious web applications
https://emberjs.com
MIT License
22.45k stars 4.21k forks source link

[Bug] No way to reset query parameters when transitioning to the same page #20211

Open fastfedora opened 2 years ago

fastfedora commented 2 years ago

🐞 Describe the Bug

Ember uses sticky query parameters by default. To make a parameter non-sticky, the documentation says there are two options:

  1. explicitly pass in the default value for that query param into <LinkTo /> or transitionTo.
  2. use the Route.resetController hook to set query param values back to their defaults before exiting the route or changing the route's model.

However, when transitioning to the same page, both options must be used. It is not sufficient to merely reset the query parameters in Route.resetController.

More specifically, there is no way to reset all parameters when transitioning to a route, as Route.resetController only resets the parameters when transitioning from a route.

Explicitly passing in default values in <LinkTo /> or transitionTo will work, but is fragile, as it relies on the caller knowing what query parameters have been defined on the route.

πŸ”¬ Minimal Reproduction

See this Ember Twiddle for an example.

This is a simplified example of an issue we're having in our current application. The links are contained within a navigation menu, so shouldn't have any knowledge of the implementation of the profile page. However, the only link that works is when the navigation menu knows exactly what query parameters have been defined for the route.

πŸ˜• Actual Behavior

Query parameters are sticky, even if you attempt to make them non-sticky using Route.resetController, when transitioning to the same page.

πŸ€” Expected Behavior

There should be a way to specify query parameters as non-sticky so that links to a route don't require knowledge of what query parameters have been defined on that route to get a default view.

Ideally there would be a way to turn off sticky behavior by default for query parameters, either at the route level or for the entire application. For links, there should be a way to request a route without existing parameter values being copied into the request.

🌍 Environment

βž• Additional Context

This is similar to issue #10260.

One workaround is to override the router service so that when an explicitly empty queryParams object is passed to initiate a transition, existing query parameters are not copied to the to transition:

// app/services/router.js
import { default as EmberRouterService } from '@ember/-internals/routing/lib/services/router';

export default class RouterService extends EmberRouterService {
  transitionTo(...args) {
    const query = args[args.length - 1]?.queryParams;
    const hasQuery = typeof query === 'object';
    const emptyQuery = hasQuery && Object.keys(query).length === 0;
    const routeOrUrl = args.length > 0 || !hasQuery ? args[0] : '';

    if (
      !emptyQuery ||
      !this.currentRoute ||
      this.currentRoute.name !== routeOrUrl
    ) {
      return super.transitionTo(...args);
    }

    const queryParams = Object.keys(this.currentRoute.queryParams).reduce(
      (params, key) => {
        params[key] = undefined;

        return params;
      },
      {}
    );

    return super.transitionTo(...[...args.slice(0, -1), { queryParams }]);
  }
}

With this change, <LinkTo @route="my-route" @query={{hash}}> would reset the query parameters, while <LinkTo @route="my-route"> would not.

This could be implemented directly within Ember with little disruption, since it's unlikely existing code is passing an empty hash into @query.

chriskrycho commented 1 year ago

Thanks for filing this!

This could be implemented directly within Ember with little disruption, since it's unlikely existing code is passing an empty hash into @query.

You might be surprised. πŸ˜‰ Suffice it to say that in the current state of the router, basically any change breaks something, and with enough users nearly any behavior you can think of does actually occur in the wild. An example of how this one could come up pretty easily:

import class Component from '@glimmer/component';

export default class Example extends Component {
  get queryParams() {
    // construct a QP object... which might be empty!
  }
}
<LinkTo @route='some.route' @query={{this.queryParams}}>Whoops!</LinkTo>

We’re working on improving/fixing the router situation, though!

spuxx1701 commented 1 year ago

Just want to +1 on this. :)

mamsoudi commented 1 year ago

Same here +1;

walter-unit4 commented 1 year ago

+1

RobbieTheWagner commented 5 days ago

Just ran into this issue as well. There is really no easy way to remove query params upon entering the route if they are invalid.