Skip to main content

This site requires you to update your browser. Your browsing experience maybe affected by not having the most up to date version.

We've moved the forum!

Please use forum.silverstripe.org for any new questions (announcement).
The forum archive will stick around, but will be read only.

You can also use our Slack channel or StackOverflow to ask for help.
Check out our community overview for more options to contribute.

Form Questions /

Moderators: martimiz, Sean, Ed, biapar, Willr, Ingo, swaiba

[Solved] Ajax contact form


Go to End


8 Posts   4625 Views

Avatar
mi32dogs

Community Member, 75 Posts

11 August 2015 at 7:31am

Hi,

I have a contact form on the homepage and it works fine, but I want to make to submit the form via AJAX.
So I added a script that does that, I have that same script running on a different site here on my local system and it is working fine.
But if I add it to this site I can’t get it to work, can anybody gives me some pointers why it is not working?

Homepage controller

  private static $allowed_actions = array(
      'ContactForm',
      'handleContactForm'
  );

  
  public function ContactForm(){
    $form = Form::create(
        $this,
        __FUNCTION__,
        FieldList::create(
            LiteralField::create('wrapper', '<div class="col-md-6">'),
            TextField::create('Name','Name')->setAttribute('placeholder', 'Name'),
            EmailField::create('Email','Email')->setAttribute('placeholder', 'Email'),
            TextField::create('Phone','Phone')->setAttribute('placeholder', 'Phone'),
            LiteralField::create('wrapper', '</div>'),
            LiteralField::create('wrapper', '<div class="col-md-6">'),
            TextareaField::create('Message','Message')->setAttribute('placeholder', 'Message'),
            LiteralField::create('wrapper', '</div>')
        ),
        FieldList::create(
            LiteralField::create('wrapper', '<div class="col-md-12 text-center">'),
            FormAction::create('handleContactForm','Submit')->addExtraClass('submit')
                ->setUseButtonTag(false),
            LiteralField::create('wrapper', '</div>')
        ),
        RequiredFields::create('Name','Email','Message')
    )
        ->addExtraClass('form general-validate');


    Requirements::javascript('mysite/site/page/contact.js');

    // load form state
    $data = Session::get("FormData.{$form->getName()}.data");

    $form->setTemplate('ContactForm');

    return $data ? $form->loadDataFrom($data) : $form;

  }

  public function handleContactForm($data, $form) {

    // set form state
    Session::set("FormData.{$form->getName()}.data", $data);

    $email = new Email();

    $email->setTo('my@domain.com');
    $email->setFrom($data['Email']);
    $email->setSubject("[Website feedback] Message from {$data["Name"]}");

    $messageBody = "
            <p><strong>Name:</strong> {$data['Name']}</p>
            <p><strong>Phone:</strong> {$data['Phone']}</p>
            <p><strong>Message:</strong> {$data['Message']}</p>
       ";
    $email->setBody($messageBody);
    $success = $email->send();


    // clear form state if success
    Session::clear("FormData.{$form->getName()}.data");
    if ($success) {
      $output['status'] = 'success';
      $output['message'] = 'You message has been send';
    }else{
      $output['status'] = 'danger';
      $output['message'] = 'You message has NOT been send';
    }

    if(Director::is_ajax()) {
      return json_encode($output);
    }else{
      $form->sessionMessage('Thanks for your submission, your message has been send','good');
      return $this->redirectBack();
    }

  }

contact.js


$(function() {

    // Get the form.
    var form = $('#Form_ContactForm');

    // Get the messages div.
    var formMessages = $('#form-messages');

    // Set up an event listener for the contact form.
    $(form).submit(function(e) {
        // Stop the browser from submitting the form.

        e.preventDefault();

        // Serialize the form data.
        var formData = $(form).serialize();
        var url = $(form).attr('action');

        // Submit the form using AJAX.
        $.ajax({
            type: 'POST',
            url: url,
            data: formData,
            dataType: 'json'
        })
            .done(function(response) {
                // Make sure that the formMessages div has the 'success' class.
                if(response.status == 'danger'){
                    $(formMessages).addClass('alert alert-danger');
                }else{
                    $(formMessages).removeClass('alert alert-danger');
                    $(formMessages).addClass('alert alert-success');
                    // Clear the form.
                    $('#Form_ContactForm_Name').val('');
                    $('#Form_ContactForm_Email').val('');
                    $('#Form_ContactForm_Phone').val('');
                    $('#Form_ContactForm_Message').val('');
                }
                $(formMessages).html(response.message);

            })
            .fail(function(data) {
                // Make sure that the formMessages div has the 'error' class.
                $(formMessages).removeClass('alert alert-success');
                $(formMessages).addClass('alert alert-danger');

                // Set the message text.
                if (data.responseText !== '') {
                    $(formMessages).text(data.responseText);
                } else {
                    $(formMessages).text('Oops! An error occurred and your message could not be sent.');
                }
            });

    });

});

Thanks

Avatar
Pyromanik

Community Member, 419 Posts

12 August 2015 at 12:48am

The first thing to do is to check that the script is actually being included in the output.
The second thing to do is to check that jQuery is included in the output... BEFORE the contact.js is.
You probably just need to include jQuery with another Requirements:: call just before the one you have currently for the contact script.

Avatar
mi32dogs

Community Member, 75 Posts

12 August 2015 at 4:51am

Sorry I should have been way more specific.

First of all if I do not include contact.js and have it working normally (no ajax) it is working fine, so the form & handler are working.

If I include the script it is submitting the form with ajax (jQuery and the script are included), but it is always returning the fail state with a message “There has been an error” and that is the only message I get.

First I thought it was maybe the url, in the ajax call I use $(form).attr('action') but if I hard code the url in there I get the same error.
Just to test if it is submitting to the right page, I tried taking out the $allowed_actions array and then I get a “Action 'ContactForm' isn't allowed on class HomePage_Controller” error, so it is submitting to the right page.

I’m at a loss why it is submitting fine as a regular form but with ajax it always goes to the fail stated.

Avatar
Pyromanik

Community Member, 419 Posts

12 August 2015 at 10:13pm

Enable dev mode, should get more specific results.
Inspect the result of the submission with your browser's dev tools. It should show the full error. You need to be on the network tab (filter to XHR to make it easier).

Avatar
mi32dogs

Community Member, 75 Posts

13 August 2015 at 5:26am

Thanks again Pyromanik,

It was a double mistake, first of all I forgot to switch off the ajax datatype json, so it always go’s to fail state if it is not receiving json, then second I thought the site was in dev mode, but I found out that I did it the wrong way, after that I got some error messages.

To style the form I wrapped the FormAction in a LiteralField, for some reason this is fine if you use it in the regular way but if you use it in a ajax call you get a error.

Moved it to the template and it is working fine now.

Avatar
Pyromanik

Community Member, 419 Posts

13 August 2015 at 9:46pm

Edited: 13/08/2015 9:47pm

Oh, if you're relying on the action name, then this will be the issue. Usually it's fine if there's only one action because it's used as the default, but perhaps your literal fields are confusing matters.

A form will send in the clicked action as a valid form field - this defines which FormAction to 'run'.
A non-clicked action is not a valid input. Since you're calling the submit programmatically, the button is not encoded as a parameter (default for jQuery.serialize() - https://api.jquery.com/serialize/ see the note on 'successful controls'). Thus the form does not know which action to execute, and basically does nothing, which is an error. You could try manually setting the default action (I think that's a thing - check the API) to work around the issue if it is the literalfields causing the issue.

Otherwise, not sure. So long as you get it working then that's fine :)

As a side note though, don't name your literal fields all the same thing. Think of the name like a Key in an array, they should be unique.
I see what you're doing with them though... try CompositeField with addAttribute() instead perhaps.

Avatar
mi32dogs

Community Member, 75 Posts

14 August 2015 at 4:54pm

hi Pyromanik,

thanks for the tip on using the CompositeField instead of the literal fields, this is a better way to add a wrapper around a bunch of fields.

Just out of interest I tried to use it to wrap the submit button into a div with it, see if that would work but no luck, I got the same error
“Uncaught Exception: Object->__call(): the method 'actionname' does not exist on 'CompositeField'”

Avatar
Pyromanik

Community Member, 419 Posts

17 August 2015 at 11:41pm

Yeah, it's because it's nested I guess.
I guess it only looks 1 level deep, where there is only a compositefield, which is not an action.
Depends on the call it uses. One searches recursively, one doesn't, iirc.