getgrav / grav-plugin-form

Grav Form Plugin
http://getgrav.org
MIT License
53 stars 79 forks source link

Template_format doesn't work (impossible to return json) #221

Closed jimblue closed 5 years ago

jimblue commented 6 years ago

For an ajax contact form, I need to access the return data as json.

To do so I'm trying to use form-message.json.twig by setting template_format to json in the page header. Sadly it doesn't seems to work.

https://learn.getgrav.org/content/headers#template-format

Thank you for you help.

PS: comming from #212

rhukster commented 6 years ago

Sorry not gotten chance to look at it. I think it is a bug though, will mark it as such.

rhukster commented 6 years ago

The template only provides the name.. if you request a .json URL it will render with the template + .json.html. Alternatively, you can override the extension with template_format: json in the page header.

jimblue commented 6 years ago

As I said using template_format: json in the page header doesn't work, for classic or modular page the result is always html... template_format: json don't override the extension!

jimblue commented 6 years ago

@rhukster here are more informations to help you understand the bug:

The page header:

---
title: Contact
forms:
    contact-form:
        name: contact-form
        template: form-messages
        template_format: json
        fields:
[...]
---

And the response:

screen shot 2018-03-16 at 07 02 57

As you can see the response is in html, not in json. Grav wrongly used form-message.html.twig over form-message.html.json

Cheating Grav:

I've try to cheat Grav to understand the bug by replacing the content of form-message.html.twig by the content of form-message.json.twig.

Modified form-message.html.twig:

{% include 'partials/form-messages.json.twig' %}
{% do http_response_code(form.responseCode) %}

And the response:

screen shot 2018-03-16 at 07 04 07

It's working, the response is in json format!

This confirm grav-plugin-form doesn't respect template_format settings in the head.

Thanks for your help!

jimblue commented 6 years ago

This is kind of critical bugs as it induces $this->grav['uri']->extension() to always return html when it should return json.

I've found that two plugins are affected by this bug, but probably others are too:

jimblue commented 6 years ago

Hi there! Just coming around to get some update, do you need more informations to fix this? Cheers

1aurabrown commented 6 years ago

I would be interested in seeing a fix to this!

jimblue commented 6 years ago

Hi @rhukster !

Don't want to bother you but do you plan to fix this? It's a pretty old bug on his way for his 8 months birthday 🎉 😭 !

Cheers

jimblue commented 5 years ago

Is there a fix for this in future v3.0.0?

jimblue commented 5 years ago

I request a json with Axios but still getting html out:

axios({
      data: new FormData(this.form),
      method: this.form.getAttribute('method'),
      url: this.form.getAttribute('action'),
      headers: {
        'Content-Type': 'application/json',
        'X-Requested-With': 'XMLHttpRequest'
      }
    })

To fix this bug I add + '.json'after the action url... not ideal at all:

axios({
      data: new FormData(this.form),
      method: this.form.getAttribute('method'),
      url: this.form.getAttribute('action') + '.json', // <<<<=== fix
      headers: {
        'Content-Type': 'application/json',
        'X-Requested-With': 'XMLHttpRequest'
      }
    })
rhukster commented 5 years ago

I have a commit that should fix this.. it's in the Grav core though.. Take a look: https://github.com/getgrav/grav/commit/005f626b886e1fbde24a00a286cf8a05280e0bf3

jimblue commented 5 years ago

I've just copy paste your code in my grav to test it and it's not working. With and without template_format: json in front-matter it's always returning HTML.

Still I've look at the code of your comit to understand it. It seems that it return by default template_format, if not found mime type else html right?

Don't get why it's not working, did you try to make an ajax request with axios on your side?

jimblue commented 5 years ago

What I use to test the response from Grav form:

import axios from 'axios'

const ajaxForm = () => {
  const form = document.querySelector('form[name="subscription-form"]')

  axios({
    data: new FormData(form),
    method: form.getAttribute('method'),
    url: form.getAttribute('action'),
    headers: {
      'Content-Type': 'application/json',
      'X-Requested-With': 'XMLHttpRequest'
    }
  })
    .then(response => console.log(response))
    .catch(error => {
      // Log errors if on development
      if (error.response) {
        // The request was made and the server responded with a status code
        // that falls out of the range of 2xx
        console.error(error.response.statusText)
      } else if (error.request) {
        // The request was made but no response was received
        // `error.request` is an instance of XMLHttpRequest in the browser and an instance of
        // http.ClientRequest in node.js
        console.error(error.request)
      } else {
        // Something happened in setting up the request that triggered an Error
        console.error('Error', error.message)
      }
    })
}
jimblue commented 5 years ago

I think we need a better way to check if the request is Ajax. I found something pretty solid used be Laravel project here:

https://github.com/laravel/framework/blob/e32e92706fe3ba26aef496d23a452750a4653725/src/Illuminate/Http/Request.php#L167

I hope this help.

Cheers

rhukster commented 5 years ago

If you don't provide an extension to the URL and provide the content-type: application/json in the request header, it will return JSON properly. At least it did in my testing.

rhukster commented 5 years ago
image 2018-11-09 at 10 21 40 pm
rhukster commented 5 years ago

Actually thinking about this, perhaps we should prefer accept: header, and use content-type: as a fallback as really accept: is what tells the preferred response format, and content-type: is for the request (which often matches the response, but not always).

jimblue commented 5 years ago

If you don't provide an extension to the URL and provide the content-type: application/json in the request header, it will return JSON properly. At least it did in my testing.

@rhukster, I just made a clean grav website from scratch with latest develop release including your last commit for this bug.

And it's still not working on my side.

To be sure everything is well done on the JS part, I've been testing the response with Axios. It's working with all my Laravel project 100% of the time.

Maybe you should have a look to the link I send you above that show how Laravel get to know if the request is Ajax or not.

PS: sending you an archive of my grav setup on discord

jimblue commented 5 years ago

Now working with latest 1.6.🔥 🔥 🔥!!!!!

Just need to set accept header in Axios:

axios({
    data: new FormData(form),
    method: form.getAttribute('method'),
    url: form.getAttribute('action'),
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-Requested-With': 'XMLHttpRequest'
    }
  })

Thanks @rhukster !!

sunilsmail commented 4 years ago

i am getting same issue

Frontmatter:

title: 'Sample Feed' name: Sample id: '1234' price: '12' description: sa template_format: json forms: contact-form: name: contact-form template: form-messages template_format: json image: assets/uploads/Port.png: name: Port.png type: image/png size: 93379 path: assets/uploads/Port.png

rhukster commented 4 years ago

Uggg. please format your YAML within a code block: https://docs.github.com/en/free-pro-team@latest/github/writing-on-github/creating-and-highlighting-code-blocks

mahagr commented 3 years ago

Also please create a new issue as this one has been closed for a couple of years. You can reference this issue from the new one.