Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Discussion options

👋🏻

One of the reasons I am a big fan of Unpoly is that it (mostly) allows you to progressively enhance an existing server-rendered multi-page app. By just adding up.link.config.followSelectors.push('a[href]') and configuring an up-main target, the app immediately starts to "feel" like a SPA.

My usual approach is to not use Unpoly for form submissions: instead, just allow the browser to submit the form as normal. Because form submissions are generally less common than navigating around an application by following links, this normally works fine and doesn't interrupt the flow. We almost never use the Unpoly server protocol.

We use Django as our backend, and the pattern ingrained into every Django developer is to use post-redirect-get for successful form submissions. By default, Django doesn't do anything special with form errors in terms of HTTP status: if there's a validation error, it rerenders the form with a 200 status code. On successful submission, it 302 redirects to somewhere else.

However, sometimes you do need to submit a form through Unpoly. An example of this from the project I'm working on currently: imagine a CRUD interface with a list of widgets at /widgets/, and a link above the list which takes you to a form to add a new widget at /widgets/add/. But instead of navigating to a different page, the form needs to appear in a modal. Validation errors in the form should keep the modal open, but a successful submission should close the modal and refresh the list view to show the newly added row.

This almost, but not quite, works without any backend changes. I have added up-submit to the <form>, and on the link I've added up-layer="new" up-accept-location="/widgets/". When the form is submitted successfully, the backend redirects to /widgets/, and the modal closes. As per the docs, I've also added up-on-accepted="up.reload('[up-main]')"> to reload the list.

However, this feels a bit janky to me. For one thing, there's a double GET request: the browser (transparently) follows the 302 from a successful POST /widgets/add/ to GET /widgets/, but the response from that GET request is thrown away, and then the up-on-accepted handler triggers and Unpoly requests GET /widgets/ again to refresh the list.

Would it instead be possible to somehow tell Unpoly that it should use the response received from a successful form submission as if it had been triggered by following a link?. So in my example above: the modal would close, and Unpoly would then replace the up-main on the parent layer with the first response that came back from the successful form submission?

I think you could almost do this without having to make any changes to the backend at all. I believe that the XHR interface doesn't expose the information about whether a request has been redirected (although I think fetch does), but I don't think that matters because you can use the URL to determine whether the submission was successful: if the URL was the same as the original URL that the form was loaded from then it must be an error response, but if the URL is different then it must be a successful submission (ie a redirect has happened). This must be possible because otherwise up-accept-location wouldn't work.

I'm hoping that the above might be possible with a bit of custom JavaScript that I can write, but I can't quite figure it out. If not, is it worth considering adding something like the above to Unpoly itself, so it just works out of the box? Maybe <a href="/widgets/add/" up-accept-location="/widgets/" up-use-response-on-accept> (or something less verbose!)

Anyway, any thoughts gratefully received 🙂

You must be logged in to vote

Replies: 2 comments · 6 replies

Comment options

Just had another thought: the proposed solution I've given above is specific to layers, but maybe it shouldn't be. Maybe the "navigate on successful submission" should be configured on the form rather than the layer-opening link. Something like up-use-submit-response (I can't think of a better name!) that would tell Unpoly to (in effect) follow the redirect by using the response that comes back from the form submission as if the request had been triggered by a link.

You must be logged in to vote
0 replies
Comment options

I think I've finally figured out how to make this work:

up.on('up:request:loaded', (event: any) => {
  if (
    event.request.method === 'POST' &&
    event.response.method === 'GET' &&
    event.request.url !== event.response.url
  ) {
    up.render('[up-main]', {
      document: event.response.text,
      layer: 'root',
    });
  }
});
You must be logged in to vote
6 replies
@triskweline
Comment options

I recommend moving your handler to up:fragment:loaded. There you can call event.preventDefault() to prevent the initial render from happening.

@j4mie
Comment options

@triskweline thanks for the tip. I just gave it a go using up:fragment:loaded but the event.preventDefault() seems to also cancel the modal acceptance (ie the modal stays open). Do I need to do that manually somehow?

@alexandremjacques
Comment options

This solution works fine but the URL won't change to the final destination (redirected URL) after render. Is there a way to change it ?
I've tried { navigate: true } and { history: true } with no success. My guess is that I should do it manually but I guess I can't find the right place to do it.

@adam12
Comment options

@alexandremjacques I might try setting the X-Up-Location header first and see if that helps.

@alexandremjacques
Comment options

@adam12 Thanks but, actually, I just rolled down the docs a little further and found the { location } option. That did the trick.

For reference:

up.render('[up-main]', {
    history: true, // this gotta be used together with { location }
    location: event.response.url,
    document: event.response.text,
    layer: 'root'
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Category
💡
Ideas
Labels
None yet
4 participants
Morty Proxy This is a proxified and sanitized view of the page, visit original site.