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

handle get forms in addition to links #272

Open
grosser opened this issue Apr 29, 2017 · 12 comments
Open

handle get forms in addition to links #272

grosser opened this issue Apr 29, 2017 · 12 comments

Comments

@grosser
Copy link

@grosser grosser commented Apr 29, 2017

would be nice to have a standardized solution for get forms ... atm using this to make all get forms use turbolinks

// https://github.com/turbolinks/turbolinks/issues/272
// support remote get forms ... since turbolinks fails to do this properly
$(document).on('submit', 'form[method=get][data-remote=false]', function(e) {
  e.preventDefault();
  var form = $(this);
  Turbolinks.visit(form.attr("action") + form.serialize());
});
@grosser grosser changed the title form with method get does not update the page handle get forms in addition to links May 6, 2017
@stefatkins
Copy link

@stefatkins stefatkins commented Aug 15, 2017

My solution in es6 and without jQuery

for(let i = 0; i < document.forms.length; i++) {
    const form = document.forms[i]
    if (form.method == "get" && form.dataset['remote'] == "true") {
      form.addEventListener("submit", (e) => {
        e.preventDefault();
        const entries = [...new FormData(e.target).entries()]
        const params = "?" + entries.map(e => e.map(encodeURIComponent).join('=')).join('&')
        Turbolinks.visit(form.action + params);
      });
    };
  };

@fadec
Copy link

@fadec fadec commented Mar 12, 2018

I tried stefan694's solution but it hits the server twice.

But this works.

e.preventDefault()
e.stopImmediatePropagation()

Yet simply

e.stopImmediatePropagation()

alone causes a page reload (bypasses turbolinks)

It really would be nice to have the framework take care of this. It's a pretty standard way to handle a search form I think.

@thelucid
Copy link

@thelucid thelucid commented Feb 20, 2019

Any better workarounds for this in the core yet?

@thelucid
Copy link

@thelucid thelucid commented Feb 20, 2019

Right, for anyone who comes across this and needs to achieve the same with the latest Rails UJS (vanilla js version), I ended up with quite a clean solution after some digging:

import Turbolinks from 'turbolinks';

document.addEventListener('turbolinks:load', function(event) {
  for (let form of document.querySelectorAll('form[method=get][remote=true]')) {
    form.addEventListener('ajax:beforeSend', function (event) {
      const detail = event.detail,
            xhr = detail[0], options = detail[1];

      Turbolinks.visit(options.url);
      event.preventDefault();
    });
  }
});

See blog post: Handling GET remote forms with Turbolinks in Rails 6.

@glaszig
Copy link

@glaszig glaszig commented Jul 26, 2019

@thelucid actually, you will want to grab forms with the data-remote attribute. took me 3 minutes to spot ;)

 import Turbolinks from 'turbolinks';

 document.addEventListener('turbolinks:load', function(event) {
-  for (let form of document.querySelectorAll('form[method=get][remote=true]')) {
+  for (let form of document.querySelectorAll('form[method=get][data-remote=true]')) {
     form.addEventListener('ajax:beforeSend', function (event) {
       const detail = event.detail,
             xhr = detail[0], options = detail[1];

@thelucid
Copy link

@thelucid thelucid commented Jul 29, 2019

@glaszig Good spot!

@Dantemss
Copy link

@Dantemss Dantemss commented Sep 12, 2019

These solutions have some weird behavior (caching issues?) when the form redirects to itself (e.g. a search form).
Try a few different values and then the page will start changing to incorrect values the moment you submit the search and change again later to correct values when the request actually comes back.

@Freika
Copy link

@Freika Freika commented Apr 3, 2020

Right, for anyone who comes across this and needs to achieve the same with the latest Rails UJS (vanilla js version), I ended up with quite a clean solution after some digging:

import Turbolinks from 'turbolinks';

document.addEventListener('turbolinks:load', function(event) {
  for (let form of document.querySelectorAll('form[method=get][remote=true]')) {
    form.addEventListener('ajax:beforeSend', function (event) {
      const detail = event.detail,
            xhr = detail[0], options = detail[1];

      Turbolinks.visit(options.url);
      event.preventDefault();
    });
  }
});

See blog post: Handling GET remote forms with Turbolinks in Rails 6.

Thanks, mate, it worked! Will there be a fix any time soon?

@thelucid
Copy link

@thelucid thelucid commented Apr 4, 2020

@Freika I believe there are some significant changes coming to Turbolinks that will address this in a different way. I’ll post again if I hear more.

@patorash
Copy link

@patorash patorash commented Apr 17, 2020

I'm not good at English, so I'm using DeepL to convert Japanese into English. I'm sorry if my grammar is wrong.

I think the selector data-remote=true is wrong, the setting data-remote=true was originally used to rewrite part of the screen using Ajax. Therefore, we don't think it's something to use in Turbolinks.visit.
As @grosser writes, data-remote=false is probably correct. However, if you set local=true in the form_with method, the attribute data-remote=false is not added.
So, I think we should try to exclude data-remote=true.
In my environment, this code works as expected.

import Turbolinks from 'turbolinks';

document.addEventListener('turbolinks:load', function(event) {
    for (let form of document.querySelectorAll('form[method=get]:not([data-remote=true])')) {
        form.addEventListener('submit', function (event) {
            event.preventDefault();
            const entries = [...new FormData(event.target).entries()];
            const actionUrl = new URL(event.target.action);
            const currentUrl = new URL(location.href);
            // if pathname not changed, hand over per parameter to next page.
            if (actionUrl.pathname === currentUrl.pathname && currentUrl.searchParams.has('per')) {
                actionUrl.searchParams.set('per', currentUrl.searchParams.get('per'))
            }
            entries.forEach(entry => actionUrl.searchParams.set(...entry));
            Turbolinks.visit(actionUrl.toString());
        });
    }
});

@glaszig
Copy link

@glaszig glaszig commented Apr 17, 2020

@patorash has a point. data-remote=true is somewhat exclusive to ujs...

@yagudaev
Copy link

@yagudaev yagudaev commented Apr 16, 2021

Tiny modification to @patorash solution if we have this UI:
Screen Shot 2021-04-16 at 1 23 43 PM

We get the following:

Screen Shot 2021-04-16 at 1 18 42 PM

Thus the same key is set multiple times -- which means only the last value will be used. Instead of entries.forEach(entry => actionUrl.searchParams.set(...entry)); => entries.forEach(entry => actionUrl.searchParams.append(...entry));. Append will do the trick well.

So here we have:

import Turbolinks from 'turbolinks';

document.addEventListener('turbolinks:load', function(event) {
    for (let form of document.querySelectorAll('form[method=get]:not([data-remote=true])')) {
        form.addEventListener('submit', function (event) {
            event.preventDefault();
            const entries = [...new FormData(event.target).entries()];
            const actionUrl = new URL(event.target.action);
            const currentUrl = new URL(location.href);
            // if pathname not changed, hand over per parameter to next page.
            if (actionUrl.pathname === currentUrl.pathname && currentUrl.searchParams.has('per')) {
                actionUrl.searchParams.set('per', currentUrl.searchParams.get('per'))
            }
            entries.forEach(entry => actionUrl.searchParams.append(...entry));
            Turbolinks.visit(actionUrl.toString());
        });
    }
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
10 participants