# Submit a form using kirby-\>email() with AJAX

**URL:** <https://forum.getkirby.com/t/submit-a-form-using-kirby-email-with-ajax/28774>\
**Category:** Questions\
**Tags:** v3\
**Created:** [June 9, 2023, 6:44am UTC](https://forum.getkirby.com/t/submit-a-form-using-kirby-email-with-ajax/28774 "2023-06-09T06:44:09Z")\
**Posts on this page:** 16\
**Page:** 1

<div class="post-metadata">

**Author:** ![anta](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.getkirby.com/anta/32/13394_2.png) [@anta](https://forum.getkirby.com/u/anta)\
**Post date:** [June 9, 2023, 6:44am UTC](https://forum.getkirby.com/t/submit-a-form-using-kirby-email-with-ajax/28774/1 "2023-06-09T06:44:09Z")

</div>

I’ve been trying to find what would be the best way to handle an AJAX form without refreshing the page on submit and keep using the same structure in [this doc reference](https://getkirby.com/docs/cookbook/forms/basic-contact-form) by using _kirby-\>email()_ with a controller.

Does someone have some experience on this?

Thanks in advance

---

<div class="post-metadata">

**Author:** ![texnixe](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.getkirby.com/texnixe/32/5754_2.png) [@texnixe](https://forum.getkirby.com/u/texnixe)\
**Post date:** [June 9, 2023, 7:45am UTC](https://forum.getkirby.com/t/submit-a-form-using-kirby-email-with-ajax/28774/2 "2023-06-09T07:45:48Z")

</div>

You could use a content representation (json controller) that is called by your JS.

Example of such a setup can be found in the AJAX load more cookbook recipe:

> **[Load more with Ajax](https://getkirby.com/docs/cookbook/templating/ajax-load-more)**
>
> Add a load more button to your listings and append items via Ajax.

---

<div class="post-metadata">

**Author:** ![anta](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.getkirby.com/anta/32/13394_2.png) [@anta](https://forum.getkirby.com/u/anta)\
**Post date:** [June 9, 2023, 8:04am UTC](https://forum.getkirby.com/t/submit-a-form-using-kirby-email-with-ajax/28774/3 "2023-06-09T08:04:35Z")

</div>

Thank you.  
So what I have is the next.

- A controller _contact.json.php_
- A form in the _contact.php_ template
- A JS snippet that handles the AJAX

It keeps giving me a 200 error on submitting, but I think I am getting closer.  
I assume the problem recalls in the controller itself, which looks like this:

```auto
<?php
return function($kirby, $pages, $page) {

    $alert = null;

    if($kirby->request()->is('POST') && get('submit')) {

        // check the honeypot
        if(empty(get('website')) === false) {
            go($page->url());
            exit;
        }

        $data = [
            'email' => get('email'),
            'text' => get('text'),
            'newsletter' => (!empty(get('newsletter'))) ? get('newsletter') : [],
        ];

        $rules = [
            'email' => ['required', 'email'],
            'text' => ['required', 'minLength' => 3, 'maxLength' => 3000],
        ];

        $messages = [
            'email' => 'Please enter a valid email address',
            'text' => 'Please enter a text between 3 and 3000 characters'
        ];

        // authenticate as almighty
        $kirby->impersonate('kirby');

        // some of the data is invalid
        if($invalid = invalid($data, $rules, $messages)) {
            $alert = $invalid;

            // the data is fine, let's send the email
        } else {
            try {
                $kirby->email([
                    'template' => 'contact',
                    'from' => 'myemail@gmail.com',
                    'replyTo' => $data['email'],
                    'to' => 'myemail@gmail.com',
                    'subject' => 'New contact request',
                    'data' => [
                        'text' => esc($data['text']),
                        'newsletter' => implode(', ', $data['newsletter']),
                    ]
                ]);

            } catch (Exception $error) {
                if(option('debug')):
                    $alert['error'] = 'The form could not be sent: <strong>' . $error->getMessage() . '</strong>';
                else:
                    $alert['error'] = 'The form could not be sent!';
                endif;
            }

            // no exception occurred, let's send a success message
            if (empty($alert) === true) {

               $success = kirby()->email([
                    'from' => 'myemail@gmail.com',
                    'to' => $data['email'],
                    'subject' => 'New contact request received',
                    'body' => 'We have received your request.',
                ])->isSent();

                $data = [];

            }
        }
    }

    return [
        'alert' => $alert,
        'data' => $data ?? false,
        'success' => $success ?? false,
    ];
};

```

Any idea how I can tweak this code to pass the data properly for the JSON and how to call it in the ‘url’ for the AJAX request?

Thanks again!

---

<div class="post-metadata">

**Author:** ![texnixe](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.getkirby.com/texnixe/32/5754_2.png) [@texnixe](https://forum.getkirby.com/u/texnixe)\
**Post date:** [June 9, 2023, 8:39am UTC](https://forum.getkirby.com/t/submit-a-form-using-kirby-email-with-ajax/28774/4 "2023-06-09T08:39:57Z")

</div>

1. Your json controller has to return json, not a PHP array.
2. You call `$kirby->email()` twice
3. As url, you have to call `contact.json`

---

<div class="post-metadata">

**Author:** ![anta](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.getkirby.com/anta/32/13394_2.png) [@anta](https://forum.getkirby.com/u/anta)\
**Post date:** [June 9, 2023, 9:02am UTC](https://forum.getkirby.com/t/submit-a-form-using-kirby-email-with-ajax/28774/5 "2023-06-09T09:02:04Z")

</div>

**1. Your json controller has to return json, not a PHP array.**

Would something like this be a good way to approach this instead (despite is not generating the JSON file for some reason)?

```
// Prepare the JSON response
$response = [
    'alert' => $alert,
    'data' => $data ?? false,
    'success' => $success,
];

$jsonResponse = json_encode($data); // Encode the response as JSON

// Save the JSON response as a file in the main directory
$filename = 'contact.json';
file_put_contents($filename, $jsonResponse);

```

**2. You call `$kirby->email()` twice**

My idea is to send an automatic email to the user after receiving the form submit. Is in this context calling the function twice something bad?

---

<div class="post-metadata">

**Author:** ![anta](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.getkirby.com/anta/32/13394_2.png) [@anta](https://forum.getkirby.com/u/anta)\
**Post date:** [June 9, 2023, 9:58am UTC](https://forum.getkirby.com/t/submit-a-form-using-kirby-email-with-ajax/28774/6 "2023-06-09T09:58:15Z")

</div>

I think I managed to make the this relation work, but I am getting an ‘{alert: null, data: false, success: false}’ on accessing the json based on this:

```auto
<?php
return function($kirby, $pages, $page) {

    $alert = null;

    if($kirby->request()->is('POST') && get('submit')) {

        // check the honeypot
        if(empty(get('website')) === false) {
            go($page->url());
            exit;
        }

        $data = [
            'email' => get('email'),
            'text' => get('text'),
            'newsletter' => (!empty(get('newsletter'))) ? get('newsletter') : [],
        ];

        $rules = [
            'email' => ['required', 'email'],
            'text' => ['required', 'minLength' => 3, 'maxLength' => 3000],
        ];

        $messages = [
            'email' => 'Please enter a valid email address',
            'text' => 'Please enter a text between 3 and 3000 characters'
        ];

        // authenticate as almighty
        $kirby->impersonate('kirby');

        // some of the data is invalid
        if($invalid = invalid($data, $rules, $messages)) {
            $alert = $invalid;

            // the data is fine, let's send the email
        } else {
            try {

                $kirby->email([
                    'template' => 'contact',
                    'from' => '...',
                    'replyTo' => $data['email'],
                    'to' => '...,
                    'subject' => '...,
                    'data' => [
                        'text' => esc($data['text']),
                        'newsletter' => implode(', ', $data['newsletter']),
                    ]
                ]);

            } 

            // no exception occurred, let's send a success message
            if (empty($alert) === true) {
                $data = [];
            }
        }
    }

    // return [
    // 'alert' => $alert,
    // 'data' => $data ?? false,
    // 'success' => $success ?? false
    // ];

    // Create the JSON response
    $response = json_encode([
        'alert' => $alert,
        'data' => $data ?? false,
        'success' => $success ?? false
    ]);

    // Write the JSON response to a file
    $file = 'contact.json';
    file_put_contents($file, $response);

};

```

Any idea what might be originating the data and success values to be always false?

---

<div class="post-metadata">

**Author:** ![texnixe](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.getkirby.com/texnixe/32/5754_2.png) [@texnixe](https://forum.getkirby.com/u/texnixe)\
**Post date:** [June 9, 2023, 10:32am UTC](https://forum.getkirby.com/t/submit-a-form-using-kirby-email-with-ajax/28774/7 "2023-06-09T10:32:51Z")

</div>

I don’t understand the purpose of the json file.

However, I made a mistake.

Your controller has to return an array, then your template has to echo the json encoded result.

The alternative would be to handle this from a route.

---

<div class="post-metadata">

**Author:** ![anta](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.getkirby.com/anta/32/13394_2.png) [@anta](https://forum.getkirby.com/u/anta)\
**Post date:** [June 9, 2023, 10:40am UTC](https://forum.getkirby.com/t/submit-a-form-using-kirby-email-with-ajax/28774/8 "2023-06-09T10:40:45Z")

</div>

I tried to reproduce a basic example of what I have, and made a mistake on renaming the JSON filename.

So my idea is to generate a JSON file from the controller (contact.php) that would be (contact.json), to then grab this information via an AJAX call.

That won’t work, is that what you meant?

Thanks again, and sorry for the insistence, this is driving me crazy.

---

<div class="post-metadata">

**Author:** ![texnixe](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.getkirby.com/texnixe/32/5754_2.png) [@texnixe](https://forum.getkirby.com/u/texnixe)\
**Post date:** [June 9, 2023, 10:43am UTC](https://forum.getkirby.com/t/submit-a-form-using-kirby-email-with-ajax/28774/9 "2023-06-09T10:43:23Z")

</div>

> [@anta](#):
>
> That won’t work, is that what you meant?

No, because when you just call this file, it is static and the controller won’t be triggered.

---

<div class="post-metadata">

**Author:** ![anta](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.getkirby.com/anta/32/13394_2.png) [@anta](https://forum.getkirby.com/u/anta)\
**Post date:** [June 9, 2023, 11:02am UTC](https://forum.getkirby.com/t/submit-a-form-using-kirby-email-with-ajax/28774/10 "2023-06-09T11:02:08Z")

</div>

OK, so what I should have is a _/controllers/contact.php_ that returns the arrays in PHP, then a _/controllers/contact.json.php_ _and a ‘/templates/contact.json.php’_ in addition to the ‘/templates/contact.php’ which stores the form. Is that correct?

One thing I don’t understand from the documentation you sent is ’ This content representation will be available at the url `localhost/photography.json` and is the URL we will use in the JavaScript in the next step.’ When is this json file being generated?

---

<div class="post-metadata">

**Author:** ![texnixe](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.getkirby.com/texnixe/32/5754_2.png) [@texnixe](https://forum.getkirby.com/u/texnixe)\
**Post date:** [June 9, 2023, 11:17am UTC](https://forum.getkirby.com/t/submit-a-form-using-kirby-email-with-ajax/28774/11 "2023-06-09T11:17:02Z")

</div>

> [@anta](#):
>
> When is this json file being generated?

This is not a file, it is a combination of a contact.json.php controller and a contact.json.php template. See the docs about content representations to understand this better.

contact.json.php controller

```auto
<?php

return function ($kirby, $page) {
	
       // other code
	return [
		'alert' => $alert,
		'success' => $success,
                // etc.
	];
};

```

contact.json.php template

```auto
<?php
echo json_encode([
  'success' => $success,
  'alert' => $alert,
  // etc.
]);

```

---

<div class="post-metadata">

**Author:** ![anta](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.getkirby.com/anta/32/13394_2.png) [@anta](https://forum.getkirby.com/u/anta)\
**Post date:** [June 9, 2023, 11:26am UTC](https://forum.getkirby.com/t/submit-a-form-using-kirby-email-with-ajax/28774/12 "2023-06-09T11:26:08Z")

</div>

OK, I think Im getting slowly there. Thanks for the patience.

So, should the `url` value for the AJAX call in the JS still be `contact.json` or something else? I am currently doing` 'form.attr('action') + '.json';` which retrieves the $page-\>url() from the form action; but shows now the `'{alert: null, data: false, success: false}'` again on submitting – but at least seems it’s finding the json file.

---

<div class="post-metadata">

**Author:** ![texnixe](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.getkirby.com/texnixe/32/5754_2.png) [@texnixe](https://forum.getkirby.com/u/texnixe)\
**Post date:** [June 9, 2023, 11:58am UTC](https://forum.getkirby.com/t/submit-a-form-using-kirby-email-with-ajax/28774/13 "2023-06-09T11:58:16Z")

</div>

> [@anta](#):
>
> form.attr(‘action’) + ‘.json’;

That should work.

But are you sending any data to the url?

---

<div class="post-metadata">

**Author:** ![anta](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.getkirby.com/anta/32/13394_2.png) [@anta](https://forum.getkirby.com/u/anta)\
**Post date:** [June 9, 2023, 12:03pm UTC](https://forum.getkirby.com/t/submit-a-form-using-kirby-email-with-ajax/28774/14 "2023-06-09T12:03:20Z")

</div>

Sharing my full code at this point for full context (not including the form which essentially just calls to the page url on the ‘action’ attribute):

/controllers/contact.json.php:

```auto
<?php
return function($kirby, $pages, $page) {

    if($kirby->request()->is('POST') && get('submit')) {

        // check the honeypot
        if(empty(get('website')) === false) {
            go($page->url());
            exit;
        }

        $data = [
            'email' => get('email'),
            'text' => get('text'),
        ];

        $rules = [
            'email' => ['required', 'email'],
            'text' => ['required', 'minLength' => 3, 'maxLength' => 3000],
        ];

        $messages = [
            'email' => 'Please enter a valid email address',
            'text' => 'Please enter a text between 3 and 3000 characters'
        ];

        // authenticate as almighty
        $kirby->impersonate('kirby');

        // some of the data is invalid
        if($invalid = invalid($data, $rules, $messages)) {

            // the data is fine, let's send the email
        } else {

            try {
                $kirby->email([
                    'template' => 'contact',
                    'from' => 'myemail@gmail.com',
                    'replyTo' => $data['email'],
                    'to' => 'myemail@gmail.com',
                    'subject' => 'Testing',
                    'data' => [
                        'text' => esc($data['text']),
                    ]
                ]);

            }

            catch (Exception $error) {}

        }
    }

    return [
        'data' => $data ?? false,
        'success' => $success ?? false
    ];

};

```

/templates/contact.json.php:

```auto
<?php

echo json_encode([
    'data' => $data ?? false,
    'success' => $success ?? false
]);

?>

```

js function:

```auto
$(form').on('submit', function(e) {
  e.preventDefault();
  // define some variables
  var form = $(this);
  // var url = form.attr('action');
  var url = form.attr('action') + '.json';
  var data = form.serialize();

  $.ajax({
    type: 'POST',
    dataType: 'json',
    url: url,
    data: data,
    // if the ajax call is successful ...
    success: function(response) {
  
    console.log(response);

      // if registration was successful
      if(response.success) {
        console.log(response.success);
      }

      // if registration failed
      if(response.error) {
       console.log(response.error);
      }

    },
    error: function (xhr, ajaxOptions, thrownError) {
        console.log(xhr.status);
        console.log(thrownError);

      }
  });
});

```

---

<div class="post-metadata">

**Author:** ![anta](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.getkirby.com/anta/32/13394_2.png) [@anta](https://forum.getkirby.com/u/anta)\
**Post date:** [June 12, 2023, 10:25am UTC](https://forum.getkirby.com/t/submit-a-form-using-kirby-email-with-ajax/28774/15 "2023-06-12T10:25:44Z")

</div>

@texnixe I have the impression that I am not passing any data to the form. Do you know what would be the best way to pass the kirby-\>email() to be triggered on the ajax call? Thanks a lot

---

<div class="post-metadata">

**Author:** ![texnixe](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.getkirby.com/texnixe/32/5754_2.png) [@texnixe](https://forum.getkirby.com/u/texnixe)\
**Post date:** [June 12, 2023, 11:06am UTC](https://forum.getkirby.com/t/submit-a-form-using-kirby-email-with-ajax/28774/16 "2023-06-12T11:06:54Z")

</div>

Vanilla JS:

```auto
  let contactForm = document.querySelector('form');

  const handleForm = async (e) => {
    e.preventDefault();
    let url = `${window.location.href}.json`;
    const formData = new FormData(contactForm);
    try {
      const response = await fetch(url, {
        method: "post",
        body: formData,
      });
      const { success, alert } = await response.json();
      
      // show error/success etc.
    } catch (error) {
      console.log('Fetch error: ', error);
    }
  }
  loginForm.addEventListener('submit', handleForm);

```
