Create a gist now

Instantly share code, notes, and snippets.

Submit data from html form to Google Doc Spreadsheet. Uses Bootstrap components for auto-complete region list and date selection.
$(function(){
var formUrl = '/* ex: https://docs.google.com/a/developmentseed.org/spreadsheet/formResponse?formkey=... */';
// Set up map
var m = mapbox.map('map').addLayer(mapbox.layer().id(' /* mapbox-account.id */ '));
// Set up map ui features with point selector
var ui = mapbox.ui().map(m).auto().pointselector(function(d) {
// Remove all points except the most recent
for (var i = 0; i < d.length - 1; i++) {
var locs = ui['_pointselector'].locations();
ui['_pointselector'].deleteLocation(locs[i]);
}
saveLatLon(d[0]);
});
// Get region data from google spreadsheet and set up LGA typeahead
mapbox.converters.googledocs('/* spreadsheet key */', '/* sheet number */', typeAhead);
// Set up date pickers
// Uses http://www.eyecon.ro/bootstrap-datepicker
var now = new Date();
now = now.getMonth() + '/' + now.getDate() + '/' + now.getFullYear();
$('/* Selectors for date fields */').val(now).datepicker();
// Handle form submission
$('form').submit(function(e) {
var button = $('input[type=submit]', this),
data = $(this).serialize();
e.preventDefault();
if (validate($(this))) {
button.button('loading');
$.ajax({
type: 'POST',
url: formUrl,
data: data,
complete: function() {
button.button('reset');
// After submission, redirect to main page
window.location = 'index.html#new';
}
});
}
// All fields are required
function validate(form) {
$('.control-group').removeClass('error');
$('input, textarea', form).each(function() {
var tag = $(this)[0].tagName.toLowerCase(),
type = $(this).attr('type');
// Validate radio buttons
if (tag === 'input' && type === 'radio') {
var name = $(this).attr('name');
if ($('[name="' + name + '"]:checked').length < 1) {
$(this).parent().parent().parent().addClass('error');
}
}
// Validate text fields
if ((tag === 'input' && type === 'text') || tag === 'textarea') {
if ($(this).val() === '' && !$(this).parent().hasClass('radio')) {
$(this).parent().parent().addClass('error');
}
}
});
if ($('.control-group.error').length < 1) return true;
$('.control-group.error').length
$('html, body').animate({
scrollTop: $('.control-group.error').offset().top - 20
}, 500);
return false;
}
});
// Use typeahead to select region
// Uses http://twitter.github.com/bootstrap/javascript.html#typeahead
function typeAhead(features) {
var lgas = [];
// Pluck `region, state` values
for (var i = 0; i < features.length; i++) {
lgas.push(features[i].properties.lgastate);
}
$('/* Element for typeahead */').typeahead({source: lgas}).change(function() {
var position = $.inArray($(this).val(), lgas);
if (position >= 0) {
var coords = features[position].geometry.coordinates,
loc = { lon: coords[0], lat: coords[1] };
saveLatLon(loc);
m.center(loc).zoom(7);
$('#map-control').show();
}
});
}
function saveLatLon(loc) {
$('#entry_1').val(loc.lon);
$('#entry_2').val(loc.lat);
}
});
@dhcole
Owner

Background on using custom forms for Google Docs: http://mapbox.com/blog/2012-09-05-mapping-crowdsourced-locations-google-docs/

Background on mapping a Google Docs spreadsheet with MapBox.js: http://developmentseed.org/blog/2012/sept/13/mapping-dynamic-data-google-spreadsheet/

@dhcole
Owner

Form markup should match id and name with a standard Google Doc form, and there should be no validation on the Google Doc form, so submissions will not be filtered. ex:

<input type="text" class="input-xlarge" id="entry_0" name="entry.0.single">
@selasegithub

Hi Dhcole, thanks for your post. It was really helpful in getting me to integrate my custom forms with google sheets however, I have problems getting the textarea, radio button and text input validations to work. I cant figure out what i am doing wrong. I also noticed that each time my form is submitted and i click the google link to submit another entry, it reverts to the default google form instead of my custom form. Is there any work around this and kindly please take a look at my code below and offer some tips on getting the validations to work. thanks.

 <script type="text/javascript">
    // Handle form submission
    $('#ss-form').submit(function(e) {
        var button = $('input[type=submit]', this),
            data = $(this).serialize();

        e.preventDefault();
        if (validate($(this))) {
            button.button('loading');
            $.ajax({
                type: 'POST',
                url: "https://docs.google.com/forms/d/1HNUn0m70hGAiewLV2WfarDtu4kwLJoJrmtS0PjX3RAM/formResponse",
                dataType: "xml",
                data: data,
                complete: function() {
                    button.button('reset');
                    // After submission, redirect to main page
                    window.location = 'http://qwameselase.wix.com/black-n-beautiful#!contact/c1n3j';
                }
            });
        }

        // All fields are required
        function validate(#ss-form) {
            debug: true,
            errorClass:'error help-inline',
            validClass:'success',
            errorElement:'span',        
            $('.control-group').removeClass('error');
            $('input, textarea', #ss-form).each(function() {

                var tag = $(this)[0].tagName.toLowerCase(),
                    type = $(this).attr('type');

                // Validate radio buttons
                if (tag === 'input' && type === 'radio') {
                    var name = $(this).attr('name');
                    if ($('[name="' + name + '"]:checked').length < 1) {
                        $(this).parent().parent().parent().addClass('error');
                    }
                }

                // Validate text fields
                if ((tag === 'input' && type === 'text') || tag === 'textarea') {
                    if ($(this).val() === '' && !$(this).parent().hasClass('radio')) {
                        $(this).parent().parent().addClass('error');
                    }
                }

                // Validate email fields
                if (tag === 'input' && type === 'email'){
                    function validateEmail(email) {
                        var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
                        return re.test(email);
                    }

                }

            });

            if ($('.control-group.error').length < 1) return true;
            $('.control-group.error').length

            $('html, body').animate({
                scrollTop: $('.control-group.error').offset().top - 20
            }, 500);

            return false;
        }
    });
</script>

<form id="ss-form" class="form-inline" method="POST" onsubmit="" action="https://docs.google.com/forms/d/1HNUn0m70hGAiewLV2WfarDtu4kwLJoJrmtS0PjX3RAM/formResponse?embedded=true">
    <fieldset>
        <legend>Personal Details</legend>
        <div class="control-group">
            <label class="control-label" for="entry_244604360">First Name</label>
                <div class="controls">
                    <input id="entry_244604360" type="text" name="entry.244604360" value="" maxlength="100">
                </div>
        </div><br>
        <div class="control-group">
            <label class="control-label" for="entry_403969165">Last Name</label>
                <div class="controls">
                    <input id="entry_403969165" type="text" name="entry.403969165" value="" maxlength="100">
                </div>
        </div><br>
        <div class="control-group">
            <label class="control-label" for="entry_1520202759">E-Mail</label>
                <div class="controls">
                    <input id="entry_1520202759" type="email" name="entry.1520202759" value="">
                </div>
        </div><br>
        <div class="control-group">
            <label class="control-label" for="entry_294268486">Contact Number</label>
                <div class="controls">
                    <input id="entry_294268486" type="text" name="entry.294268486" value="" maxlength="15">
                </div>
        </div>                                          
    </fieldset><Br>
    <fieldset>
        <legend>Canvas Type</legend>
        <div class="control-group">
            <label for="entry_1505307714">Canvas Type</label><br><br>
                <input id="group_1123680937_1" type="radio" name="entry.1123680937" value="Booking Only">Book a spot, pick canvas at event RM 50<br><br><br>
                <label>
                    <input id="group_1123680937_2" type="radio" name="entry.1123680937" value="4x4single" />
                    <img src="http://static.wixstatic.com/media/253d02_dec03f3ad51c403e945281f6ee98240f.jpg_srb_p_600_556_75_22_0.50_1.20_0.00_jpg_srb" style="max-width:15%" alt="40x40x1 / Single / RM 195">
                </label>
                <label>
                    <input id="group_1123680937_3" type="radio" name="entry.1123680937" value="4x4diptych" />
                    <img src="http://static.wixstatic.com/media/253d02_805d3463f30f48d9b814791099c23a95.jpg_srb_p_746_378_75_22_0.50_1.20_0.00_jpg_srb" style="max-width:30%" alt="40x40x2 / Diptych / RM 325">
                </label>
                <label>
                    <input id="group_1123680937_4" type="radio" name="entry.1123680937" value="4x4triptych" />
                    <img src="http://static.wixstatic.com/media/253d02_133bac604e744812aaa82e8c11f6d63b.jpg_srb_p_1106_409_75_22_0.50_1.20_0.00_jpg_srb" style="max-width:40%" alt="40x40 / Triptych / RM 455">
                </label><br><br><br>
                <label>
                    <input id="group_1123680937_5" type="radio" name="entry.1123680937" value="4x9single" />
                    <img src="http://static.wixstatic.com/media/253d02_add84feb205b4ffe9d2a31bf6ff5d7a6.jpg_srb_p_600_567_75_22_0.50_1.20_0.00_jpg_srb" style="max-width:15%" alt="40x90 / Single / RM 350">
                </label>
                <label>
                    <input id="group_1123680937_6" type="radio" name="entry.1123680937" value="4x9diptych" />
                    <img src="http://static.wixstatic.com/media/253d02_22045721297f4b3c9e8f1c46d9bb0bec.jpg_srb_p_600_567_75_22_0.50_1.20_0.00_jpg_srb" style="max-width:30%" alt="40x90 / Diptych / RM 610">
                </label>
                <label>
                    <input id="group_1123680937_7" type="radio" name="entry.1123680937" value="4x9triptych" />
                    <img src="http://static.wixstatic.com/media/253d02_cffba0d8eee742f2b24c815b310240f0.jpg_srb_p_704_525_75_22_0.50_1.20_0.00_jpg_srb" style="max-width:40%" alt="40x90 / Triptych / RM 865">
                </label><br><br><br>
                <label>
                    <input id="group_1123680937_8" type="radio" name="entry.1123680937" value="6x10single" />
                    <img src="http://static.wixstatic.com/media/253d02_28767a3b1e694403846cb80f04ab1338.jpg_srb_p_600_567_75_22_0.50_1.20_0.00_jpg_srb" style="max-width:20%" alt="60x100 / Single / RM 525">
                </label><br>

        </div>
    </fieldset><Br>
    <fieldset>
        <legend>Remarks</legend>
        <div>
            <label for="entry_508497383">Comments</label><br>
            <textarea id="entry_508497383" rows="3" cols="100" name="entry.508497383" value=""></textarea>
        </div>
    </fieldset><Br>
    <fieldset>
        <legend>Get in Touch</legend>
        <div>
            <label for="entry_1014633536">Notify me of upcoming workshops?</label><br>

            <input id="group_1558721004_1" type="radio" name="entry.1558721004" value="Yes" /> Yes <Br>             
            <input id="group_1558721004_2" type="radio" name="entry.1558721004" value="No" /> No <Br>           
        </div>
    </fieldset><Br><Br>
    <div style="text-align: center;">
        <input type="submit" name="submit" value="Submit" id="ss-submit">
    </div>
</form>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment