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

Accepting multipart/form-data in addition to src URL #199

Open
kjacobson opened this issue May 14, 2020 · 5 comments
Open

Accepting multipart/form-data in addition to src URL #199

kjacobson opened this issue May 14, 2020 · 5 comments
Labels
enhancement Acknowledged nice-to-have

Comments

@kjacobson
Copy link

kjacobson commented May 14, 2020

Premises

  • SPAs have caused developers to write a lot of code, much of which parallels behavior implemented natively in the browser:
    • Reacting to a link click with a URL change
    • Reacting to a URL change with an HTTP request
    • Reacting to an HTTP response by rendering new HTML
    • Reacting to a form submission by serializing form data and sending an HTTP request with that form data
  • A big reason SPAs have come to exist is that browser makers do not use the document frame to provide indications that a request for a new HTML document is in progress, so traditional requests feel slow (typically they use the tab bar, the URL bar, and the little ribbon in the lower left that replaced the status bar).
  • It's not really true that AJAX/Fetch requests for JSON which is used to generate HTML client side take less time than traditional HTTP requests for an HTML document. The appeal of AJAX/Fetch requests with client-side HTML-generation is mostly about distracting/assuaging users with loading indicators.
  • Browsers are built to understand that when an HTTP request returns an HTML document, that response document should be rendered to the screen automatically (for the most part this works with images, JSON documents, XML, and sometimes PDFs too). When an AJAX/Fetch request returns an HTML document, the browser doesn't do this.
  • There are several cases where a browser will automatically make an HTTP request, and render the response document to the screen:
    • link clicks
    • URL changes (URL bar)
    • URL changes (programmatic via JS or meta tag)
    • form submission
  • While preload behavior is a big benefit of portals, there are existing ways to achieve preloading of HTML documents that are linked to. That portals can be animated before, during, or after document loading is sort of the killer feature here in terms of capturing mindshare from SPAs.

Thesis
To further advance this work, I think it's crucial to be able to provide similar transition behavior when the loading and rendering of a new HTML document comes as the result of a form submission, rather than a link click.

A functioning server-rendered web application will have to be able to handle HTTP requests with multipart form-data encoding and respond with new HTML documents, e.g.:

  • A search form is submitted and I see the search results page, but with the new search term filled in an a new list of results.
  • A new user form is submitted and I am redirected to the "details" page for that user (if successful).

These experiences won't get the transition advantage portals provide without writing a bunch of JavaScript to do things the browser does natively. Again:

  • encoding form data
  • making a request to the form action URL with the form data
  • rendering the response document to the screen

So it's not that there is no way whatsoever in the current spec to animate the appearance of a portal which displays the HTML document provided by the server as the response body of a form submission HTTP request. It's that to do so given the current spec would require writing a lot of JS to do things the browser is already pretty good at doing.

It is true that there's no way to get the preload behavior, as form submissions require user input and are not therefore primed for a static request.

Possible solutions (for discussion at least)
You currently can set portal src programmatically:
portal.src = 'https://en.wikipedia.org/wiki/World_Wide_Web'

Portals could support programmatically setting action (instead of src) to a URL and setting formData to a FormData object and handling submission from there. This still means we have to write some JS to serialize the form, but new FormData(document.getElementById('myForm')) is not bad.

Alternately, you could be able to reference a form element directly from the portal with a for attribute (like the label HTML element has). Basically, portals for forms and portals for links.
<form-portal for="myForm">

It seems this should probably be limited to same-site form actions.

@jeremyroman
Copy link
Collaborator

Firstly, thank you for your well-argued issue. :)

I think there are a few ways this could be achieved.

  1. Allowing explicitly navigating a portal through requests other than a plain GET of a URL, e.g. with FormData as you suggest, or with a fetch Request which can include a POST body.
  2. The advanced version of Ephemeral navigation portals #192, where a Request could be supplied to create a portal that must immediately navigate.
  3. @jakearchibald's idea for a navigate event that would let you initiate the form submission normally, and send the in-flight navigation request into an (ephemeral as in Ephemeral navigation portals #192) <portal> element.

As for whether it should be limited to same-site form actions, I'd have to look at how form submission is controlled and whether we want same-origin (or same-site) restrictions in addition to the usual XSRF protections that are the classic if awkward solution.

Another point in favour of this, of course, is that forms can currently target the main frame and iframe (<form target=...>) and this capability would be missing from portals.

I think this is a reasonable enhancement that could eventually be explored, but I'm less certain that it's necessary to include in an initial/minimal version of this feature, especially since workarounds are available if inconvenient (e.g. using fetch to do a post in advance of loading the document via a GET, or first loading a stub document that receives a message and does a form submission within the portal). But I do agree that it is unnecessarily duplicative of logic that the UA already has well in hand.

@jeremyroman jeremyroman added the enhancement Acknowledged nice-to-have label May 14, 2020
@jeremyroman
Copy link
Collaborator

(The request approach runs into the fact that it's currently illegal to construct a Request whose mode is "navigate", though it's conceptually the best existing class to map to the concept of "an arbitrary navigation request, be it GET, POST, etc".)

@jakearchibald
Copy link
Collaborator

jakearchibald commented May 14, 2020

We'd either end up accepting Request objects and thoroughly vetting them, or introduce some new kind of API.

We don't want to create a type of navigation that can't already happen, so we'd be restricting the content type to text/plain, application/x-www-form-urlencoded, or form/multipart, and restricting the method to GET or POST.

Given the restrictions, I'm leaning towards a new API.

portal.navigate(url, {
  method, // GET or POST, defaults to GET.
  body, // Optional. Either a FormData object, or a URLSearchParams object.
});

As with the Request constructor, FormData would set the content type to form/multipart, and URLSearchParams would set the content type to application/x-www-form-urlencoded. We'd lose text/plain, but is that really a problem?

createPortalForNavigation could have the same signature.

@domenic
Copy link
Collaborator

domenic commented May 14, 2020

One of the major complicating factors in the iframes spec and implementation architecture is how it allows navigating to various non-HTTP(S) URLs, such as data:, blob:, javascript:, about:blank, as well as the srcdoc="" feature. I am hopeful we can find a way to achieve the use cases mentioned in this issue without introducing a way for portals to navigate things that are not backed by a HTTP(S) URL.

@kjacobson
Copy link
Author

Thanks for the responses. Right, I suppose that, so long as it's possible to create a JS-abetted workaround, a webapp author can maintain continuity of UX, and that's enough for a first go. What I'm thinking one doesn't want is an MPA where link clicks have slick portal transitions and form submissions give you a flash-of-blank experience.

Jake's suggestion seems like pretty good ergonomics to me:

document.getElementById('myForm').addEventListener('submit', (e) => {
  e.preventDefault()

  const portal = document.createElement('portal')
  portal.navigate(e.target.action, {
    method: e.target.method,
    body: new FormData(e.target)
  })
})

I had forgotten about form targets though:

Another point in favour of this, of course, is that forms can currently target the main frame and iframe (<form target=...>) and this capability would be missing from portals.

That seems like a maximal solution in the sense of letting the developer rely on as much native user agent behavior as possible.

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

No branches or pull requests

4 participants