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

fail when render status: :unprocessable_entity #29

Closed
nyrf opened this issue Jan 6, 2021 · 32 comments · Fixed by #34
Closed

fail when render status: :unprocessable_entity #29

nyrf opened this issue Jan 6, 2021 · 32 comments · Fixed by #34

Comments

@nyrf
Copy link

nyrf commented Jan 6, 2021

def create
    @post = Post.new(post_params)

    respond_to do |format|
      if @post.save
        format.html { redirect_to @post, notice: "Post was successfully created." }
        format.json { render :show, status: :created, location: @post }
      else
        format.html { render :new, status: :unprocessable_entity }
        format.json { render json: @post.errors, status: :unprocessable_entity }
      end
    end
  end
class Post < ApplicationRecord
  validates :title, presence: true
end

new.html.erb

<div x-data="{text: 'demo' }">
  <div x-text="text"></div>
</div>


<%= form_with(model: @post) do |f| %>
  <%= f.label :title %>
  <%= f.text_field :title %>
  <%= f.submit %>
<% end %>

when title is blank and render 422 status, alpine won't work.

@SimoTod
Copy link
Owner

SimoTod commented Jan 6, 2021

Sorry, I'm not a ruby developer but I'm not totally sure this is the right repository. Unless we are saying that it would work as expected with only alpine and without the turbolinks bridge.

By the way, could you post the generated html please? Thanks

@nyrf
Copy link
Author

nyrf commented Jan 7, 2021

hotwired/turbo#39

<div x-data="{text: 'demo' }">
  <div x-text="text">demo</div>
</div>
<form action="/posts" accept-charset="UTF-8" method="post"><input type="hidden" name="authenticity_token" value="ufX5eM9Ld2hGYyia8R4zGAS_wFvwNHzv2-CsUV0UKuR1bEpPhfuqfXDgtHTV_gDecNDNzLV-ipvSCyWkzevp-w">
   <div class="form-group">
    <label for="post_title">
    <input type="text" name="post[title]" id="post_title" />
   </div>
  <div class="form-group">
    <input type="submit" name="commit" value="Save post" class=" btn btn-primary"/>
  </div>
</form>

After submit form and sever return 422 status, alpine fail to init all data and events.

error

@SimoTod
Copy link
Owner

SimoTod commented Jan 7, 2021

Thanks, I'll have a look in the next few days and see if i can replicate.

@nyrf
Copy link
Author

nyrf commented Jan 7, 2021

Thanks, i modified

document.addEventListener('turbo:load', initCallback)
to document.addEventListener('turbo:render', initCallback) can make it works, I'm not sure if that's the right way.

@SimoTod
Copy link
Owner

SimoTod commented Jan 7, 2021

I'll need to have a look. In Turbolinks, render gets called multiple times when you have cached previews which is not the best for Alpine, maybe it has changed in Turbo. It seems weird that it calls render but it doesn't call load after maybe I can ask the guys at Turbo.

@SimoTod
Copy link
Owner

SimoTod commented Jan 7, 2021

there's a turbo:submit-end that we might be able to use

@nyrf
Copy link
Author

nyrf commented Jan 7, 2021

Thanks, you're right, i'll try again later.

@SimoTod
Copy link
Owner

SimoTod commented Jan 7, 2021

if the code use turbo frames, it replaces the content and triggers the alpine update.
The problem seems to be when you don't use a frame. In that case, it doesn't seem to replace the content with the response but it just re-renders the old age but it doesn't trigger a load event.
To be fair, it seems like a turbo bug, if the page is replaced, it should trigger the load event at the end.

@nyrf
Copy link
Author

nyrf commented Jan 7, 2021

Thanks for your reply, yes, the turbo:load won't be triggered, i will use turbo frame to submit form until there is a PR.

@KostasKostogloy
Copy link

KostasKostogloy commented Mar 10, 2021

@SimoTod turbo:submit-end doesn't seem to do the trick. I fixed it on my project by initializing alpine components on turbo:render.

turbo:render can trigger twice (when you visit a cached page) so it's not optimal, but I can't see a way around it 🤷‍♂️

If we use turbo:render listener, we can remove the one on turbo:load. Otherwise we would be initializing alpine components 2-3 times on each page visit (if we use both).

For anyone looking for a quick workaround till this is resolved, try this:

window.addEventListener('turbo:render', function (event) {
    window.Alpine.discoverUninitializedComponents((el) => {
        window.Alpine.initializeComponent(el)
    });
});

@SimoTod
Copy link
Owner

SimoTod commented Mar 10, 2021

@KostasKostogloy I know but I would really like Turbo to fix their bug or at least clarify the behaviour because it's not consistent/correct. They should either not refresh the page when there is an error or trigger a load event IMO. If you look at the bug above, people have the same issues with other frameworks, it's not just an alpine thing.
Turbo is still in beta so it may change.

Out of curiosity, is there a reason why you don't use turbo frames with forms? It seems to be the standard way to do it.

To clarify, the problem with render is that it gets called twice when you visit a cached page so, with the solution above, it will get called 3 times, not a big issue but something to be aware of.

@KostasKostogloy
Copy link

KostasKostogloy commented Mar 10, 2021

@SimoTod the main reason is that we display flash messages on the page and if we enclosed the form in a frame those would be outside of it (and not get displayed).

Problem's context: You want a form to submit with POST on the same route it gets rendered on. Turbo will not replace the dom unless you use a 422 response to trick it into doing so.

So our options were:

  1. Disable turbo on the form
  2. Do redirect on our controller to GET on the same route after doing our processing. If we did that we wouldn't be able to keep form input, which would be reset
  3. Use a frame and lose flash messages
  4. Use 422 response and add the snippet I posted above to initialize alpine

So there was no perfect solution and since 4. did not introduce any perceptible slowdown, we opted for that.

@SimoTod
Copy link
Owner

SimoTod commented Mar 10, 2021

Thanks for your feedback. I need to think about that before switching to turbo:render.

4 works but it's based on the assumption that an internal behaviour of Turbo won't ever change (I don't think they state anywhere that a 422 refreshed the page) so maybe keep an eye on it until they tag the first stable release.

@SimoTod
Copy link
Owner

SimoTod commented Apr 27, 2021

Better late than never. @nyrf @KostasKostogloy or anyone else following the bug report.
Could you test https://cdn.jsdelivr.net/gh/SimoTod/alpine-turbo-drive-adapter@bug/422-responses/dist/alpine-turbo-drive-adapter.min.js and let me know if it resolves your issues, please?
I decided not to use load because in some edge cases calling Alpine on the cached version would introduce some small bugs so I went down the turbo:submit-end route.

@KostasKostogloy
Copy link

@SimoTod thanks for your hard work 🙇‍♂️

I am going to test this in the next few days and get back to you.

@nyrf
Copy link
Author

nyrf commented Apr 28, 2021

@SimoTod Great, thanks. I've tested and it works.

@SimoTod
Copy link
Owner

SimoTod commented Apr 28, 2021

Thanks for confirming @nyrf

@rickclare
Copy link

rickclare commented Apr 29, 2021

Thanks for the fix @SimoTod. I've installed v1.0.3 of alpine-turbo-drive-adapter (upgraded from v1.0.2) and performed some initial testing myself - the problems we were having around this issue look to be fixed in Firefox (v88) and Chrome (v90).

However, for me at least, the problem still seems to exist in Safari (Safari v14.1, MacOS Big Sur 11.3). Is anyone experiencing this issue in Safari only?

(Note that the workaround fix suggested in #29 (comment) does "fix" the issue in all browsers that I've tested with, so perhaps there's some quirk of Safari that needs to be taken account of?)

@SimoTod
Copy link
Owner

SimoTod commented Apr 29, 2021

The problem with using render is that it might break the page in other edge cases introducing flickerings, etc. I'll check safari tomorrow.

@SimoTod
Copy link
Owner

SimoTod commented Apr 29, 2021

@rickclare It took less than expected. I was able to replicate it in Safari.
1.0.4 should fix the issue. Thanks for reporting it.

@rickclare
Copy link

@SimoTod that's great, thank you for jumping on the fix so quickly. I'll do some more testing tomorrow to double check v1.0.4 in the browsers that I have access to (everything popular apart from MS Edge)

Thanks again.

@rickclare
Copy link

rickclare commented Apr 30, 2021

Hi @SimoTod I've performed some more testing with v1.0.4
Unfortunately I'm still experiencing the issue in Safari only (no problems in Firefox or Chrome)

If it helps with diagnosis - my scenario is a simple user-registration page (run-of-the-mill Ruby-on-Rails MVC), which has the following markup:

<form action="/register" accept-charset="UTF-8" method="post">
  <input type="hidden" name="authenticity_token" value="0j2wnYnsareCIghBGUzTCF3qv5b51aq46Pi2E2pYxHyN4vx8u0PB1JXFBzc8VNBXxSKmA3OKcoAXsHDyoxiqrw==">

  <label for="member_email">Email</label>
  <input id="member_email" name="member[email]" type="email">

  <label for="member_password">Password</label>
  <input id="member_password" name="member[password]" type="password">

  <button type="submit">Register</button>
</form>

<div class="app-environment"
     x-data="{ visible: true }" 
     x-on:click="visible = false"
     x-show="visible">
  Development
</div>
  1. Before submitting the registration form, the "app-environment" <div> hides itself on click (as expected).

  2. After submitting the form with invalid values (i.e. resulting in a 422 response rendering the form again with validation errors) the <div> no longer responds to a click (i.e. indicating that AlpineJS is not initialising itself properly)

  3. After submitting the form with valid values (i.e. resulting in a redirect) the <div> hides itself on click (as expected).

I'm happy to keep testing any fixes that you release. I would love to help with the fix itself, but my javascript-fu isn't advanced enough to be able to suggest a solution.

@SimoTod
Copy link
Owner

SimoTod commented Apr 30, 2021

@rickclare I'm not a rail developer so I would need the response from the form submission if you can kindly send it over (The example I built in php is working okay in safari)

You can get it from safari > right click > inspect element (you need to enable it in preferences if you haven't yet) > network tab > your request. I need the content of both the preview tab and headers tab.

Thanks

@rickclare
Copy link

rickclare commented Apr 30, 2021

@SimoTod No problem, here is the response from Safari's Network tab:

Headers:

Summary
URL: https://localhost:4443/register
Status: 422
Source: Network
Address: 127.0.0.1:4443
Initiator: 
localhost:1:882


Request
:method: POST
:scheme: https
:authority: localhost:4443
:path: /register
Content-Type: application/x-www-form-urlencoded;charset=UTF-8
Accept: text/vnd.turbo-stream.html, text/html, application/xhtml+xml
Accept-Encoding: gzip, deflate, br
Accept-Language: en-gb
Host: localhost:4443
Origin: https://localhost:4443
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1 Safari/605.1.15
Connection: keep-alive
Referer: https://localhost:4443/register
Content-Length: 121
Cookie: _secret_key=SFMyNTY.g3QAAAABbQAAAAtfY3NyZl90b2tlbm0AAAAYSHNheFYySzVWVVZfdC1BQVFXd3VYU1Y1.Rpw0UKoGbo3ZvQa47y-y9Y8W047AMMJDIUuVfUoIPbc
X-CSRF-Token: cEEvCgR3DG17BBIYAmIiDR0OMxIANQVx82NrREGX-QDGvOcLLYDgXfSD

Response
:status: 422
Content-Type: text/html; charset=utf-8
X-XSS-Protection: 1; mode=block
Cache-Control: max-age=0, private, must-revalidate
Date: Fri, 30 Apr 2021 09:28:51 GMT
Content-Length: 4386
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
x-download-options: noopen
x-permitted-cross-domain-policies: none
cross-origin-window-policy: deny
x-request-id: FnqYt9qh_WhxX8QAAABH

Request Data
MIME Type: application/x-www-form-urlencoded;charset=UTF-8
_csrf_token: cEEvCgR3DG17BBIYAmIiDR0OMxIANQVx82NrREGX-QDGvOcLLYDgXfSD
member[email]: [email protected]
member[password]: 123456

Preview:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, viewport-fit=cover">
  <meta charset="UTF-8" content="PTgQOh91D15hPgI3N0QiESAgLhsNBRdHuKqBIGDk7kThCicPqwYnUVAr" csrf-param="authenticity_token" method-param="_method" name="csrf-token">
  <link data-turbo-track="reload" rel="stylesheet" href="/css/app.css">
  <script data-turbo-track="reload" defer src="/js/app.js"></script>
</head>
<body>

<form action="/register" accept-charset="UTF-8" method="post"><input name="authenticity_token" type="hidden" value="PTgQOh91D15hPgI3N0QiESAgLhsNBRdHuKqBIGDk7kThCicPqwYnUVAr">
  <div class="notification is-danger">
    Something went wrong! Please check the errors below
  </div>

  <label for="member_email">Email address</label>
  <input id="member_email" name="member[email]" type="email" value="[email protected]">

  <label for="member_password">Password</label>
  <input id="member_password" name="member[password]" type="password">
  <span class="is-danger">should be at least 12 characters</span>

  <button class="button is-primary" data-form-target="button" type="submit">Register</button>
</form>

<div class="app-environment"
     x-data="{ visible: true }"
     x-on:click="visible = false"
     x-show="visible">
  Development
</div>

</html>

@rickclare

This comment has been minimized.

@rickclare

This comment has been minimized.

@SimoTod
Copy link
Owner

SimoTod commented Apr 30, 2021

I believe edge doesn't support ?? so it needs to be transpiled properly. The cdn version should already be compatible with ms edge and possibly ie11

@SimoTod
Copy link
Owner

SimoTod commented Apr 30, 2021

@rickclare So... The UMD version (the one that is served by the CDN too) works correctly in Safari but the ESM version doesn't. I assume that, if you instruct your bundler to generate a file compatible with edge and safari, it would work.

However, I've recompiled with slightly different settings and the ESM version seems to behave now. For just a few bytes more, I'm happy to include an ESM version that works out of the box with Safari.

I'll run a few more tests and I'll release it over the weekend with the other pending PR to support turbo-streams. It's going to be 1.1.0

@rickclare
Copy link

@SimoTod thank you for your perseverance with this. I'll find some time tomorrow to test out the new version and report back. Have a great weekend.

@SimoTod
Copy link
Owner

SimoTod commented Apr 30, 2021

@SimoTod thank you for your perseverance with this. I'll find some time tomorrow to test out the new version and report back. Have a great weekend.

It will likely be Sunday. I decided to run a few more tests for both features first. 👍

@SimoTod
Copy link
Owner

SimoTod commented May 2, 2021

Unfortunately, the Safari was still happening (some times was working and sometimes wasn't). I believe something to do with the submit-end event being triggered on the document before the page update and Safari ignoring the delayed callback once the DOM was updated.
I gave up and switched to turbo:render :)
I found out that Turbo adds a data-turbo-preview attribute when rendering the preview from the cache while waiting for the page so I can skip the first render event when Turbo triggers them twice.

@rickclare
Copy link

rickclare commented May 3, 2021

@SimoTod I've retested the scenario where I had the original issue, using v1.1.0, and the problem is no longer there (i.e. my Alpine markup is working perfectly after submitting a form that returns a 422 response)

I successfully tested with the following browsers:

✅ Safari v14.1 (MacOS)
✅ Google Chrome v90.0 (MacOS)
✅ Firefox v88.0 (MacOS)
✅ iOS Safari v14.1 (on iPhone iOS 14.5)
✅ MS Edge v90.0 (Windows 10)
✅ Android Chrome v83 (on Android 11 - via Android Studio simulator)

Thanks again for your patience and getting this fixed so quickly, nice job.

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

Successfully merging a pull request may close this issue.

4 participants