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

Turbo not handling form submission when globally disabled but locally enabled and called with requestSubmit #392

Closed
willcosgrove opened this issue Sep 14, 2021 · 3 comments · Fixed by #406

Comments

@willcosgrove
Copy link
Contributor

willcosgrove commented Sep 14, 2021

I have Turbo drive globally disabled in my app.

Turbo.session.drive = false

I have a form that is invisible to the user, no submit button, but with data-turbo=true set on it. It is used to handle a QR code scan to send the results of the scan back to the server. So upon a QR scan, an input in the form gets set to the value of the scan, and then form.requestSubmit() is called. But Turbo does not do anything to intercept this submission. I am trying to do a turbo stream response from the server, but the turbo stream mime type is not getting added to the accept header.

I think I've tracked down the reason for this. When my form.requestSubmit() gets called, this should run:

turbo/src/core/session.ts

Lines 190 to 192 in 11840c1

willSubmitForm(form: HTMLFormElement, submitter?: HTMLElement): boolean {
return this.elementDriveEnabled(form) && this.elementDriveEnabled(submitter)
}

form will be set, but submitter will not, because I'm not passing in a submitter in my form.requestSubmit() call (primarily because I don't have a submitter, no submit button on this form currently).

this.elementDriveEnabled(form) should return true because I have data-turbo=true set on the form. But this.elementDriveEnabled(submitter) when submitter is undefined, what will it return?

turbo/src/core/session.ts

Lines 305 to 324 in 11840c1

elementDriveEnabled(element?: Element) {
const container = element?.closest("[data-turbo]")
// Check if Drive is enabled on the session.
if (this.drive) {
// Drive should be enabled by default, unless `data-turbo="false"`.
if (container) {
return container.getAttribute("data-turbo") != "false"
} else {
return true
}
} else {
// Drive should be disabled by default, unless `data-turbo="true"`.
if (container) {
return container.getAttribute("data-turbo") == "true"
} else {
return false
}
}
}

First it's closest [data-turbo] is found, which will be undefined, because it itself is undefined, and then the answer depends entirely on what Turbo.session.drive is set to, which in my case is false.

Now, what I've discovered through the process of writing this issue is that form.requestSubmit() should default submitter to the form if one is not provided, but that is not the behavior I'm seeing in the latest Chrome, or in the latest Safari. Both of them pass null as the submitter which gets turned into undefined here:

submitBubbled = <EventListener>((event: SubmitEvent) => {
if (!event.defaultPrevented) {
const form = event.target instanceof HTMLFormElement ? event.target : undefined
const submitter = event.submitter || undefined

If the browsers were behaving like the documentation says that they should, I would not have run into this issue. But, in my opinion, I think it would make sense that if there is no submitter, that the answer of willSubmitForm should be based solely on the form.

willSubmitForm(form: HTMLFormElement, submitter?: HTMLElement): boolean { 
  return this.elementDriveEnabled(form) && (!submitter || this.elementDriveEnabled(submitter))
}

// or more clearly

willSubmitForm(form: HTMLFormElement, submitter?: HTMLElement): boolean {
  if (submitter) {
    return this.elementDriveEnabled(form) && this.elementDriveEnabled(submitter)
  } else {
    return this.elementDriveEnabled(form)
  }
}

edit:

I should just also include the workaround in case anyone else is running into this. I was unable to just pass the form element as the submitter, as Chrome (maybe other browsers would have as well) was raising an error. So I added a hidden submit button and passing that as the submitter in the requestSubmit() call.

form.requestSubmit(hiddenSubmitButton)
@terracatta
Copy link
Contributor

On your new hidden submit button did you also have to add data-turbo=true? Based on my read of the current code, both the form and the submitter need this element for Turbo Drive to submit the form.

@willcosgrove
Copy link
Contributor Author

@terracatta I did not, but it is nested inside the form which has it.

I believe this is why it doesn't need it:

const container = element?.closest("[data-turbo]")

There is a parent of the new hidden submit button that has data-turbo=true and so that is sufficient to enable it for the button.

@tleish
Copy link
Contributor

tleish commented Sep 18, 2021

can you post your hidden form html?

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

Successfully merging a pull request may close this issue.

3 participants