whatwg / url

URL Standard
https://url.spec.whatwg.org/
Other
527 stars 137 forks source link

Proposal: URL.setSearchParams() #828

Open jeremy-code opened 3 months ago

jeremy-code commented 3 months ago

What problem are you trying to solve?

Effectively, for a URL object, setting the searchParams (and by extension search) to some URLSearchParams object.

Currently, the searchParams property is read-only (see: URL: searchParams property). Hence, to update the searchParams, one would have to update the search string property manually instead.

This is not ideal as developers may want to work in terms of objects rather than strings (new URLSearchParams({ foo: "bar", baz: "qux" }) is much more readable compared to "?foo=bar&baz=qux").

Being able to set the searchParams property without having to go through a string intermediate can be a pretty common case, such as appending multiple entries at once, or initializing with some default values.

What solutions exist today?

Today, to update the searchParams, one would either have to run the delete() method for all current entries and set() for all new entries (which is impractical) or update the search string property manually such as:

function setUrlSearchParams(url, searchParams) {
  if (searchParams.size === 0 || searchParams === null) {
    url.search = "";
  }
  url.search = "?" + searchParams.toString();
}

However, since URLSearchParams.toString() and URL.search are not the same value, this is slightly more complicated

The toString() does not include the question mark ?, while URL.search does. This doesn't mean just by appending "?" that the proper value, since for both, an empty string is equivalent to a null query.

How would you solve it?

I am not certain on the implementation details of the function, since just implementing the setUrlSearchParams() shown earlier method seems redundant (as you would have to parse the string into a URLSearchParams object to get the new .searchParams property), but in general, having a function URL.setSearchParams can make some tasks easier.

For instance, for the cases I mentioned:

Intializing a URL with default values

// previously, with string immediate
const url = new URL(`https://example.com?${new URLSearchParams({ foo: "bar", baz: "qux" })}`);
const url = new URL("https://example.com?foo=bar&baz=qux");

// with a setSearchParams() method
const url = new URL("https://example.com")
url.setSearchParams(new URLSearchParams({ foo: "bar", baz: "qux" }))

Appending multiple entries

// url already has search parameters
const url = new URL(`https://example.com?${new URLSearchParams({ foo: "bar", baz: "qux" })}`);
url.setSearchParams(new URLSearchParams([["foo", "bar"], ["baz", "qux"], ...url.searchParams]));

Anything else?

EDIT: I apologize, I just noticed this is very similar to #413.

annevk commented 3 months ago

We could maybe add a searchParams a setter. I don't think we need a separate method. The one question I see is what should happen if URLSearchParams already has an associated URL. We'd have to decide between throwing or changing the URL association (and updating both the old and new URL). I don't think we want to end up in a situation where a single URLSearchParams can be associated with multiple URLs.

jasnell commented 3 months ago

I think a searchParams setter makes the most sense but I also don't see this as a critical issue to address and would likely prefer not to change this.

The one question I see is what should happen if URLSearchParams already has an associated URL. We'd have to decide between throwing or changing the URL association (and updating both the old and new URL). I don't think we want to end up in a situation where a single URLSearchParams can be associated with multiple URLs.

I think there are a few options here.

  1. The setter could be defined to essentially clone/copy the given URLSearchParams as opposed to storing it by reference. It could be a bit odd in some cases in that the following would hold true url.searchParams = newSearchParams; url.searchParams !== newSearchParams but it would address the URL link issue.

  2. Throw if the URLSearchParams has a link to a URL such that users would need to do something like url.searchParams = new URLSearchParams(otherUrl.searchParams)

  3. Make it possible to detach a URLSearchParams from it's URL, e.g. url.searchParams = otherUrl.searchParams.detach() ... which would effectively null out the search params on the otherUrl and break that link.

Of these, number 2 would be my preference.

domenic commented 3 months ago

I'm confused by this feature request, and in particular why the search setter doesn't work today.

Given this code:

const usp = new URLSearchParams("foo=bar");
const url = new URL("https://example.com/");
url.search = usp;

you get the desired result: url is modified to have foo=bar search params.

The section "What solutions exist today?" in the OP seems to be implying this isn't good enough. But I can't understand exactly when.

Can you give a concrete example of a URLSearchParams object such that assigning url.search = usp gives the wrong result? And such that setting url.searchParams = usp, using a hypothetical future setter, would give a better result?

All the examples in "How would you solve it?" work today with the search setter.