AJAX form submission within modular page not working

I’m having trouble with getting AJAX form handling to work completely in modular pages.

I have a modular page /contact/modular.md, and several modules, including /contact/_contact-me/form.md

I understand that, since form 2.0, when using modular pages we can place the form frontmatter directly in the modular form.md. We don’t have to put the form in the modular.md that is “parent” to form.md.

However, when my /contact/_contact-me/form.md uses the code described in the docs to submit via AJAX (changing action: to point to /contact), the site does not display any message after submit, and I get a 500 Internal Server error in the browser Console for trying to access mysite.com/contact, even though that url is routable).

When I set things up the old way (form plugin < 2.0), and place the form frontmatter in /contact/modular.md, the problem vanishes: no 500 error, and the post-submit message displays.

Is it not possible to have AJAX form submission work correctly on a modular page, with the form contained in the module?

@prw,

UPDATE: Incorrect answer.

I created a small working demo using Grav 1.6.26 + Quark. It saves the data of the form and displays the message ‘Thank you for getting in touch!’

folder structure:

user/pages
├── 01.home
│   └── default.md
└── 02.modular-page
    ├── _contact
    │   └── form.md
    └── modular.md

form.md:

---
title: Contact Form

form:
    name: contact
    action: '/modular-page'
    template: modular
    refresh_prevention: true

    fields:
        name:
          label: Name
          placeholder: Enter your name
          autocomplete: on
          type: text
          validate:
            required: true

    buttons:
        submit:
          type: submit
          value: Submit

    process:
        save:
            fileprefix: contact-
            dateformat: Ymd-His-u
            extension: txt
            body: "{% include 'forms/data.txt.twig' %}"
        message: Thank you for getting in touch!
---

# Contact form

Some sample page content

Hope this helps…

Thanks, @anon76427325, I appreciate this.

I’m also confused—my impression from the docs has been that we needed some html and js code like the below (from the example in the docs) in order for the same-page confirmation message to appear.

<div id="form-result"></div>

<script>
$(document).ready(function(){

    var form = $('#ajax-test-form');
    form.submit(function(e) {
        // prevent form submission
        e.preventDefault();

        // submit the form via Ajax
        $.ajax({
            url: form.attr('action'),
            type: form.attr('method'),
            dataType: 'html',
            data: form.serialize(),
            success: function(result) {
                // Inject the result in the HTML
                $('#form-result').html(result);
            }
        });
    });
});
</script>

Did I misunderstand, or is this no longer the case and the functionality is built in to the form plugin now?

I was referring to your example in this post, which uses the html and js code. It also specifies that the form action goes to an empty pages/forms/ajax-test/form.md. I’m curious about the reasoning for that approach vs the simplicity of what you just shared. Is the earlier example no longer required?

@prw, Sorry, I might be doing something terribly wrong… I’m checking…

Ok, thank you. Still, your example does work. It’s just surprising.

@prw, Yes, the form is working, but not with Ajax… I forgot half the steps necessary and didn’t notice the form did a regular HTTP post… :man_facepalming: Sorry about the confusion.

Folder structure: (using the ‘One Page’ skeleton)

user/pages
└── 01.home
    ├── 01._hero
    ├── 02._highlights
    ├── 03._callout
    ├── 04._features
    ├── 05._contact
    │   └── form.md
    └── modular.md

Content form.md:

---
title: Contact

form:
   name: ajax-test-form
   action: '/home'
   template: form-messages
   refresh_prevention: true

   fields:
      name:
         label: Your Name
         type: text

   buttons:
      submit:
         type: submit
         value: Submit

   process:
      message: 'Thank you for your submission!'
---

<div id="form-result"></div>

<script>
$(document).ready(function(){

   var form = $('#ajax-test-form');
   form.submit(function(e) {
      // prevent form submission
      e.preventDefault();

      // submit the form via Ajax
      $.ajax({
         url: form.attr('action'),
         type: form.attr('method'),
         dataType: 'html',
         data: form.serialize(),
         success: function(result) {
            // Inject the result in the HTML
            $('#form-result').html(result);
         }
      });
   });
});
</script>

The Chrome debugger breaks at the success function of the Ajax request and the message is shown inside <div id="form-result"></div>. So it seems to be working in a modular page using Ajax.

1 Like

Thank you for spending some time with this. I have a few questions:

I notice in your most recent version, there is no action: field in the frontmatter. Is that okay? Is that only necessary if wanting to use the action: '/forms/ajax-test'?

And also on the subject of the “dummy page” pages/forms/ajax-test/form.md: I had seen a few examples, including yours from that other thread I mentioned, that use this “dummy page” with a blank form. But when I look closer at the docs, I see that they are not using a dummy page. Instead pages/forms/ajax-test/form.md in their example is the actual page containing the form, so action: '/forms/ajax-test' refers not to a dummy but to the same page.

There is a note in that docs page on ajax form submission regarding dealing with redirects:

NOTE: We use a hard-coded action: '/forms/ajax-test' so the ajax has a consistent URL rather than the letting the form set the action to the current page route. This resolves an issue with the Ajax request not handling redirects properly. This can otherwise cause issues on the 'home' page. It doesn't have to be the current form page, it just needs to be a consistent, reachable route.

So I’m confused about this dummy page idea, and wondering when or why it would be useful, since the docs are talking about an action that points to the same page the form is on. Sorry, hope that makes my question clear.

@prw, You may be entirely right and I may be entirely wrong about the ‘dummy page’ and ‘action’… In hind side, I think I’m probably wrong. The mind plays funny games sometimes :slight_smile:

How did my mind twist and turn back then?

When I tried to answer the earlier question about Ajax in a modular page, I had no experience with Forms (still don’t use them) and the docs were confusing me.

Especially the following two lines:

First, we’ll create a form in a page called: forms/ajax-test/ and create a form page called form.md.

Does the docs mean 1 page or 2 pages? Although not sure, I thought 2…

NOTE: We use a hard-coded action: '/forms/ajax-test' so the ajax has a consistent URL rather than the letting the form set the action to the current page route. […]

The above note lead me to think: “Ah, don’t point to the current page, but to the other. So the docs means two pages…”

NOTE: […] It doesn’t have to be the current form page, it just needs to be a consistent, reachable route.

Again I thought: “A second dummy page will do”

Also because why would you create a form below folder ‘/forms’ which doesn’t show up in the menu? Dummy to create a consistent URL perhaps?

So I created two forms:

  • /forms/ajax-test/form.md (the ‘dummy’ needed for the consistent URL?)
  • 01.home/_contact/form.md with the Ajax form definition…

And guess what? It worked, so I must have done something right… I thought…

I have update my previous post.

1 Like

As I followed your initial examples, with the dummy page, the logic that you just outlined seemed clear. I understand where you were coming from! It was only after looking carefully at the docs that I started to have doubts. You’re right, they leave some ambiguity—what’s the purpose of the folder /forms, as you say? It would be good to have those questions cleared up for future readers.

I’m still a beginner, but I’m wondering if the use of the dummy page “worked” because the js prevented loading of the dummy page. I don’t know!

@prw, Still in doubt… In my explanation above, I’ve added another suggestive line from the docs that my hint to using a ‘dummy’ page:

NOTE: […] It doesn’t have to be the current form page, it just needs to be a consistent, reachable route.

Also, when using action: /home instead of action: /forms/ajax-test, could the form now run into: “This can otherwise cause issues on the ‘home’ page.” ? Because /home is often redirected to /.

You said:

[…] I’m wondering if the use of the dummy page “worked” because the js prevented loading of the dummy page. I don’t know!

No that’s not it.

Ajax has to be able to reach an existing route else the server will throw 404. For example: Using action: /existing-folder/default.md will do fine, but when using action: /non-existing-folder/default.md, Ajax will throw 404 in the console.

As said in the docs “it just needs to be a consistent, reachable route”.

That also hints why /forms/ajax-test/form.md can be an invisible (albeit empty) route.

I’ll ask the devs on Github

Interesting, I see your points. I wonder why it works without an action: at all. Perhaps grav assigns a default action?