Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEATURE query-params] Removing query-params from links explicitly? #5274

Closed
ghost opened this issue Jul 29, 2014 · 14 comments
Closed

[FEATURE query-params] Removing query-params from links explicitly? #5274

ghost opened this issue Jul 29, 2014 · 14 comments

Comments

@ghost
Copy link

ghost commented Jul 29, 2014

Good work on the query-params in the 1.7 beta. It works well for my app, except for one thing that I can't figure out.

Once a query-param is set through a link...

{{link-to "myroute" (query-params filter="123"}}

...links to the same "bare" route (without query-params) now have the query params appended automatically.

I.e.

{{link-to "myroute"}}

now results in a route hash #/myroute?filter=123. OK, great.

Now my question: how do I generate query-paramless URLs once a param has been set in an earlier transition?

I tried:

{{link-to "myroute" (query-params filter=null)}}

but that leads to #/myroute?filter=undefined

Of course I can work around it like this:

{{link-to "myroute" (query-params filter="")}}

but I'd prefer a clean URL.

BTW, I tried both the hash and history-based routing strategies.

Summarizing: is there a way to clear query-params explicitly through a link-to helper? Or would this not be the framework-prescribed way of solving the problem?

@ghost
Copy link
Author

ghost commented Jul 29, 2014

Having written all this down, one way of solving this is to create a controller action that resets the query param properties.

Still, there might be use cases for resetting query params from within a link-to. For example, global navigation items that should always link to a paramless route.

@bradleypriest
Copy link
Member

@lfridael The query-params logic stores and compares the provided value against the original value.

If you set filter: null on the controller's prototype, then {{link-to "myroute" (query-params filter=null)}} will remove the QP.

e.g.

App.ApplicationController = Ember.Controller.extend({
  filter: null
})
{{#link-to "myroute" (query-params filter=null" }}

@ghost
Copy link
Author

ghost commented Jul 29, 2014

@bradleypriest Thx! That was the hint I needed.

However, using null as the default is not working for me. I have to use an empty string to get the comparison to work.

I.e.

export default Ember.Controller.extend({  
  queryParams: [ "filter" ],
  filter: ""
});
{{link-to "myroute" (query-params filter="")}}

UPDATE. Just to be clear: using an empty string as the default property value removes the query params from the link-to-generated URL. This solves the problem for me.

@wagenet wagenet changed the title Removing query-params from links explicitly? [FEATURE query-params] Removing query-params from links explicitly? Aug 1, 2014
@machty
Copy link
Contributor

machty commented Aug 3, 2014

@lfridael I think the issue is documentation; I believe your use case would be handled by using the new resetController hook to return a QP back to its default as you are leaving the route that has the QP defined.

See here for an example https://github.com/emberjs/ember.js/blob/master/packages/ember/tests/routing/query_params_test.js#L1375-L1382

Let me know if this addresses your use case (I'm doing a ton of QP work now, including documentation, so I'll make sure this is more clear)

@machty
Copy link
Contributor

machty commented Aug 3, 2014

Closing for now given that setting to default property in the link-to is working for you. At some point in the future we might add some more API but that would best start in a discuss.emberjs.com thread rather than GH issues.

@machty machty closed this as completed Aug 3, 2014
@ghost
Copy link
Author

ghost commented Aug 3, 2014

@machty Just experimented with the resetController hook. It's definitely useful, but it doesn't seem to address my particular use case. The problem being that I would want to have a way to specify "always reset QP" links, regardless of the route you're currently in. (I.e. even if if the target route is the current route.) Global links in a navbar are an example of this.

Anyway, the current API is fine, with the only minor consequence being that links might have to be aware of QP defaults. In practice, I don't see this becoming much of a problem. A global link in a navbar such as {{link-to "posts" (query-params page=1)}} is pretty much self-explanatory.

@machty
Copy link
Contributor

machty commented Aug 3, 2014

We may add something like a query-params-reset subexpression helper for this very use case, but it makes sense to feel things out a bit more before letting a potential scenario-solve into the API. Let's revisit this in the future.

@Mariusio
Copy link

I had the same problem as @lfridael and was able to solve it by using

resetController(controller, isExiting, transition) {
    if (isExiting) {
      controller.set('user_id', undefined);
    }
  }

It seems that the difference is between using undefined and null.
For null, the QP changes to ?user_id=null.
For undefined, the QP is removed.

So, setting user_id to undefined in the controller apparently removes the QP from the url in Ember 2.4.0.
I couldn't find any documentation about using undefined, do you see any downsides in using it?

@robclancy
Copy link

This is something that should have been resolved a long time ago. Ember should give the tools to do all things a user expects to happen.

When I am on github issues here and filter Bug with https://github.com/emberjs/ember.js/issues?q=is%3Aopen+is%3Aissue+label%3ABug and I click the "Issues" link I would expect to go back to the "global" issues https://github.com/emberjs/ember.js/issues, which would simply remove any query params. Github does exactly this because it is just logical.

With ember you can't do that in a "proper" way. We either have either use a custom query-params helper or specify every single possible param in every single link-to that you don't want query params on if you aren't leaving the route, this is obviously far from maintainable. We use the resetController method and even that is a pain to maintain (it's bad enough we have to define query params in multiple areas let alone reset them too).

I don't even understand any reason to use stick query params. They have caused me issues since day 1 and I haven't seen any benefits other than pagination links (which are part of the route I am in so could easily get the same benefit in other ways without ruining every link to the page).

I am just going to have to create the query-params-reset mentioned above.

@robclancy
Copy link

robclancy commented Jan 16, 2017

So other people can solve this in a hackyish way that is actually maintainable (still need to do the resetController stuff for when you transition programmatically, although with these changes you could add _explicitQueryParams to the query params hash to get the same effect as the helper). I might make an add-on if I find time.

Added a helper to use called explicit-query-params in place of query-params where you want to only use exactly what is sent in.

import Ember from 'ember';

export function explicitQueryParams(params, hash) {
  let values = Ember.assign({}, hash);
  values._explicitQueryParams = true;

  return Ember.Object.create({
    isQueryParams: true,
    values: values,
  });
}

export default Ember.Helper.helper(explicitQueryParams);

This is pretty much the same as the default (can be empty though) but we set a param we can check in the router (the hackyish part). If I turn this into an addon I will also make this helper wotk better than the super restrictive query-params one (you should be able to pass a hash in for example).

Then in your router.js (I assume you have it and are extending Ember.Router already) add the following method in the extend.

  _hydrateUnsuppliedQueryParams(state, queryParams) {
    if (queryParams._explicitQueryParams) {
      delete queryParams._explicitQueryParams;
      
      return queryParams;
    }

    return this._super(state, queryParams);
  },

This isn't the cleanest way but the "proper" way would also involve replacing the link-to component and creating classes to detect between functionality etc instead of an underscore param.

@tarponjargon
Copy link

I'm new to ember and am struggling with this too. I wrote a post about it on ember discussion forum:
https://discuss.emberjs.com/t/app-design-managing-complex-state-with-sticky-queryparams/12381

The more time I spend on it, the more I think that you have to roll your own "parameter manager" (but I still could be missing tools that may already exist). Right now when an action is triggered I'm just analyzing what params are on the url, passing them thru my custom ruleset, and outputting an entirely new url (in the route). Which makes me think I am doing things the jquery way and not the ember way.

I think it would be great if there were a built-in tools to help do this. I have no idea what they'd look like tho!

@toejiang
Copy link

thx @robclancy,
we even need no explicit-query-params when the code like:

// file router.js
  _hydrateUnsuppliedQueryParams(state, queryParams) {
    let sticky = queryParams._keep_sticky;
    delete queryParams._keep_sticky;
    return sticky ? this._super(state, queryParams) : queryParams;
  }

then all link-to sticky query params is disabled by default, and we can enable it by adding QP like:

{{#link-to route (query-params _keep_sticky=true)}}link with sticky qa{{/link-to}}

@robclancy
Copy link

robclancy commented Sep 26, 2019

My way no longer works because of an ember update. I am now overriding query-params directly. It will now always be not sticky when using query-params but have to stay sticky for when modifying params in other ways.

helpers/query-params.js

import { helper } from '@ember/component/helper';

class QueryParams {
  values = null;
  isQueryParams = true;

  constructor(values = null) {
    this.values = Object.assign({ ['-sticky']: false, values });
  }
}

export default helper(function queryParams(_, params) {
  return new QueryParams(Object.assign({}, params));
});

router.js extending

   _hydrateUnsuppliedQueryParams(state, queryParams) {
    let sticky = queryParams['-sticky'] !== false;
    delete queryParams['-sticky'];

    return sticky ? this._super(state, queryParams) : queryParams;
  },

@NullVoxPopuli
Copy link
Sponsor Contributor

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants