Coded something up in Couch in an interesting way? Have a snippet or shortcode to share? Post it here for the community to benefit.
4 posts Page 1 of 1
Hello!

Have just come across AJAX forms' validation and tried some solutions for it, but finally came to this approach. It supports multiple forms on one page. Maybe somebody will find this useful or suggest better approach.

First, define 'is_ajax' in kfunctions.php:
Code: Select all
if( !method_exists($TAGS, 'is_ajax') ) {
   $FUNCS->register_tag( 'is_ajax', 'is_ajax_handler' );
}
function is_ajax_handler( $params, $node ){
   if( !empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH'])=='xmlhttprequest' ){
      return '1';
   }
   return '0';
}


Then make your form:
Code: Select all
<style>
    .hidden {display:none}
</style>
<cms:form action='' method='post' anchor='1' class="form">
    <cms:input type="text" name="f_name" required="1" class="<cms:if k_error_f_name> error</cms:if>" />
    <cms:input type="text" name="f_phone" required="1" class="<cms:if k_error_f_phone> error</cms:if>" />
    <cms:input type="submit" name="f_submit" />
    <div class="f-success hidden">The form is sent!</div>
    <cms:if k_success>   
        <div class="f-success">The form is sent!</div>
        <cms:if "<cms:is_ajax/>"><cms:abort><cms:content_type 'application/json' />{}</cms:abort></cms:if>
        <cms:send_mail from=k_email_from to=k_email_to subject='New message'>
            <p><strong>Name:</strong> <cms:show frm_f_name /></p>
            <p><strong>Phone:</strong> <cms:show frm_f_phone /></p>
        </cms:send_mail>
    </cms:if>
    <cms:if k_error>
        <cms:capture into='errorsString'>
            <cms:if k_error_f_name>f_name => error,</cms:if>
            <cms:if k_error_f_phone>f_phone => error,</cms:if>
        </cms:capture>
        <cms:if "<cms:is_ajax/>"><cms:abort><cms:content_type 'application/json' /><cms:php>echo json_encode([<cms:show errorsString />]);</cms:php></cms:abort></cms:if>
    </cms:if>
</cms:form>


And add some simple JS (use your AJAX library):
Code: Select all
<script>
( function( window ) {
    'use strict';
    function classReg( className ) {
        return new RegExp("(^|\\s+)" + className + "(\\s+|$)");
    }
    var hasClass, addClass, removeClass;
    if ( 'classList' in document.documentElement ) {
        hasClass = function( elem, c ) {
            return elem.classList.contains( c );
        };
        addClass = function( elem, c ) {
            elem.classList.add( c );
        };
        removeClass = function( elem, c ) {
            elem.classList.remove( c );
        };
    }
    else {
        hasClass = function( elem, c ) {
            return classReg( c ).test( elem.className );
        };
        addClass = function( elem, c ) {
            if ( !hasClass( elem, c ) ) {
                elem.className = elem.className + ' ' + c;
            }
        };
        removeClass = function( elem, c ) {
            elem.className = elem.className.replace( classReg( c ), ' ' );
        };
    }

    function checkEmpty( obj ) {
        for (var i in obj) { return false; } return true;
    }

    var utils = {
        hasClass: hasClass,
        addClass: addClass,
        removeClass: removeClass,
        checkEmpty: checkEmpty
    };
    window.utils = utils;
})( window );

function clearErrors(elems) {
    if (typeof elems === 'object') {
        for (var t = 0; t < elems.length; t++) {
            utils.removeClass(elems[t], 'error');
        }
    }
}
function applyAjaxSubmit(form, successEl) {
    form.onsubmit = function() {
        var formData = {};
        for (var t = 0; t < form.elements.length; t++) {
            formData[form.elements[t].name] = form.elements[t].value;
        }
        reqwest({ // use your library ($.ajax for jQuery)
            url: '',
            method: 'post',
            data: formData,
            success: function (resp) {
                clearErrors(form.elements);
                if (!utils.checkEmpty(resp)) {
                    utils.addClass(successEl, 'hidden');
                    for (var error in resp) {
                        utils.addClass(form.querySelector('#' + error), 'error');
                    }
                } else {
                    utils.removeClass(successEl, 'hidden');
                }
            }
        });
        return false; 
    };
}
applyAjaxSubmit(document.querySelector('.form'), document.querySelector('.f-success'));
</script>


Works in IE9+, both with JS enabled or disabled.
Be careful, <cms:abort> tag works on 1.4.5RC2 version or above!

We capture errors manually and get a string
f_name => error, f_phone => error,
Then use json_encode (works on PHP 5.2+) and apply appropriate Content-type (application/json).

Previously I used an approach of getting full-page html in result of AJAX and splitting the interesting part, but new <cms:abort> tag allows to minify traffic and get clear JSON with right content-type, thanks to KK!

Updated accordingly to KK post.

Update: I got a bug with sending e-mails using AJAX. Seems that sometimes <cms:abort> is breaking further code like e-mail sending. If you get the same problem, just double <cms:send_mail> in AJAX block like:
Code: Select all
<cms:if "<cms:is_ajax/>"><cms:send_mail from=k_email_from to=k_email_to subject='New message'>
<p><strong>Name:</strong> <cms:show frm_f_name /></p>
<p><strong>Phone:</strong> <cms:show frm_f_phone /></p>
</cms:send_mail><cms:abort><cms:content_type 'application/json' />{}</cms:abort></cms:if>
Interesting ... I was just trying to get something working with intercooler.js http://intercoolerjs.org/ , but had some problems with JSON maybe ( when I study your approach better ) this could be very useful for me.

Thanks for sharing.
I load frameworks and write bugs on top of them, after that I rearrange the code so that it looks like a cool product.
One small suggestion -
Instead of returning the entire HTML page via AJAX and then parsing out a small part of it, we can use cms:abort tag instead to return only the relevant bit of data as follows -
Code: Select all
<cms:if "<cms:is_ajax/>">
    <cms:abort>
        <cms:php>
            echo json_encode([<cms:show errorsString />]);
        </cms:php>
    </cms:abort>
</cms:if>

In the code above, only the JSON encode string will be returned back.

N.B. The cms:abort tag will require atleast Couch v1.4.5RC2
(currently downloadable from viewtopic.php?f=5&t=8981).
Thanks, I've updated the solution!
4 posts Page 1 of 1
cron