Zend_Form style errors after AJAX post

For the best user experience, it's useful to post sometimes a html <form> with an asynchronous request. The javascript acquires the data, sends it to the server and waits for a response. But when the form contains errors, you need to return the messages in order to be displayed for the user.

This article provides a strategy to fetch the response, looks if the form contained errors and display the errors just like the errors would be displayed when you used the errors decorator of Zend_Form.

The server side

Most times, I add the JSON context to a certain action with the contextSwitch action helper and check if the current context is JSON:

$json = ('json' === $this->contextSwitch->getCurrentContext());

Now the variable $json is a boolean and indicates if the current request is a JSON request. Next, you'd like to post the status of the form and if there are errors, notify the client:

if ($form->isValid($this->getRequest()->getPost())) {
    // Do something with the form data
    $this->view->status   = "success";
} else {
    $this->view->status   = "error";
    $this->view->messages = $form->getMessages();
}

The client side

I am a fan of unobtrusive javascript. Therefore I hook the javascript code onto the submit event. In jQuery this would look like:

$("form").submit(function () {
    $.post($(this).attr("action") + "/format/json", $(this).serialize(), function (data) {
        // Here we have the information about the stats
    });
});

In this case, I look at every submit on every form and post it to the action of the form itself, appending the parameter to request a JSON response. Also the data I post is a serialized version of the form data. And in the end, I catch the returning data so we can check if the status is success.

The received JSON data can look like this, if the form wasn't valid because the "name" element was left empty:

{
    "status": "error",
    "messages": {
        "name": {
            "isEmpty": "Value is required and can't be empty"
        }
    }
}

In above javascript, we need to look at this reponse and take the appropriate action (where the comments are placed now). First, check the status:

if (data.status === "success") {
    // Notify the user of success
} elseif (data.status === "error") {
    $.formErrors(data.messages);
} else {
    alert('Unknown status');
}

To keep this piece of code clean and simple, I call a jQuery function, formErrors() which I'll explain in a moment. The other status is success, you can notify the user whatever you want. In this blog, the reaction a user posts is immediately placed in the list of reactions. The other possibility is the status is no "success" nor "error" so we don't know how to handle it (and simply alert the user with this message).

The last part is to write the jQuery function formErrors(). In this function we want to loop through all messages. They are listed as key/value pairs where the key is the name of the element and the value is a list of errors. This error list is also a key/value pair where the key is the error type, the value the user-friendly message.

What we'd like to do is loop through all messages and place a <ul> element just after the <input>. After that, we fill the <ul> with <li> elements and in this <li> the user-friendly message is shown. Or in code:

$.formErrors = function (data) {
    $.each(data, function (element, errors) {
        var ul = $("<ul>").attr("class", "errors");
        $.each(errors, function (name, message) {
            ul.append($("<li>").text(message));
        });

        $("#" + element).after(ul);
    });
}

Now when an error is returned, the messages are displayed in the same style as Zend_Form would do with the Errors decorator. So if you already styled the <ul> errors list, these javascript inserted errors would look the same.