Overview

If a service doesn't give you the programmer enough control over their content, write a web scraper and build an API from the raw html.

All services should make it top priority to give developers complete control over the system right away. I don't mean "make it so we can do a million things", rather, make it so we can easily access the core data in a scalable fashion. If you do that for us, we'll make you big. Google doesn't care about that though, they're already big :).

Anyway, check out the google form (before) and the styled one using the api (after):

Online Form Services

Can only view form responses by going to the Google Spreadsheet. You can tell Google Docs to send you an email whenever someone fills out a form, but you have to go to the actual spreadsheet to view the responses (waste of time).

All of them are great if you just need to get up a form to collect information and the user experience doesn't really matter.

But once user experience matters, good luck. Wufoo and Google both allow you to style the forms with CSS, but unless you're a ninja, you can't remove the copyright info and "powered by X" stuff that they all come with. To top that off, you only have 2 options for viewing the form:

On their website (definitely not an option if you want a seamless UX).

In an iframe. Needless to say, iframes are inherently limited. Good luck applying cascading styles to your form and using javascript to enhance the experience (or for client side validations!). You're stuck with the defaults...

I'd go with Google Forms if you want just a basic setup. It's free, and you don't have to worry about being charged. And while PandaForm looks very cool, we know Google Forms will be around for a while.

Build an API around Google Forms

I spent a late night once a while back seeing how Google Forms were structured. After spending a few weeks with the Surveyor Ruby Gem and seeing how they built forms with haml, I decided you could probably do that with Google Forms. Combine that with Dr. Nic's idea, and you've got a very customizable web form setup.

1. Install Googletastic

One of my clients wanted a completely custom interface to Google Apps to manage all of their documents/manuals/brochures, forms, assets, communication, etc. So I built Googletastic. Here's a Sinatra/Googletastic demo I setup randomly to show of it's features. Doesn't do much justice, but you can fork the source anyway.

Googletastic uses Nokogiri to parse XML from the Google Data API. After spending hours on this, I discovered HTTParty. Would've made my life easier, could've used JSON :). But it's done, and it works.

2. Create a Google Form

3. Convert the Google Form to JSON!

# setup credentialsGoogletastic.keys={:username=>"[email protected]",:password=>"my-password",:domain=>"my-site.org"# only if applicable}# get first form from your account (latest form)form=Googletastic::Form.firsthash=form.to_hash#=> [{:tag=>"text",:value=>[""],:help=>"",:key=>"email",:required=>false,:id=>"2",:title=>"Email"},{:tag=>"text",:value=>[""],:help=>"",:key=>"firstname",:required=>false,:id=>"0",:title=>"First Name"},{:tag=>"text",:value=>[""],:help=>"",:key=>"phone",:required=>false,:id=>"3",:title=>"Phone"}]

Here's how that works:

Grab the list of spreadsheets from your Google Spreadsheets.

Login to your Google Apps account using Mechanize.

For each spreadsheet, find the formkey by parsing random html and javascript after a few redirects (since there's no way to get a formkey from Google other than by manually going to the url or doing this).

Download the form at the url http://spreadsheets.google.com/viewform?hl=en&formkey=#{form_key}#gid=0

<divclass="errorbox-good"><divclass="ss-item ss-paragraph-text"><divclass="ss-form-entry"><labelclass="ss-q-title"for="entry_1">How could we improve?</label><labelclass="ss-q-help"for="entry_1"></label><textareaname="entry.1.single"rows="8"cols="75"class="ss-q-long"id="entry_1"></textarea></div></div></div>

So googletastic's just saying, for each <div class='ss-item'>, find the title, help text, description, default values, whether or not it's required, and the id of the entry. We know that id's look like entry_(\d+) and form field names, which are passed as params, look like entry.(\d+).(single|group). From that information, we have the entire form structure.

$(document).ready(function(){$("#ss-form").ajaxForm({success:function(responseText,statusText,xhr,$form){alert("Thanks for filling it out!\n\nMaybe one day we'll make a chart of the responses.");}});$(".ss-form-entry").each(function(){varcontext=$(this);varrequired=$(".ss-required-asterisk",context).get(0)!=null;if(required){$("textarea, input, select",context).addClass("required");}});// construct jquery-validate.js validation rulesvarrules={};$(".ss-form-entry .required").each(function(){varelement=$(this);varname=element.attr("name");rules[name]="required";});// setup validation$("#ss-form").validate({rules:rules,errorPlacement:function(error,element){varentry=element.parents(".ss-item");entry.addClass("errorbox-bad");error.insertAfter(entry.find(".ss-q-title"));}});});

Some notes:

If you're using these forms on a completely static site and don't have server-side access (Github Pages for example), then you can't do server-side validations obviously. This means that if users don't have javascript enabled (which is a tiny fraction nowadays), they could fill out forms incorrectly because they'd bypass javascript validations.

So, I recommend validating all required fields in jQuery. Otherwise, if they submit the form and something's wrong, Google will redirect them to the ugly Google Form page for them to correct invalid fields. Let's avoid that :). However, if you have server-side access, you could capture the response, extract the json, and render that using your HAML template. I'm doing that on a site now.

6. Use Google Forms in Your App

To sum up, here's all you need to use Google Forms in this completely custom way:

require'rubygems'require'googletastic'require'json'# setup oauth parametersGoogletastic.keys={:username=>"[email protected]",:password=>"mypass"}defparse_form(form)title=form.titlefile_name=title.downcase.gsub(/[\s_-]+/,"-").gsub(/[^a-z0-9-]/,"")content=""content<<"---\n"content<<"title: #{title}\n"content<<"---\n\n"content<<JSON.generate(form.to_hash)File.open(file_name,"w+"){|file|file.putscontent}en# in this example, save form json to jekyll-style postGoogletastic::Form.all.eachdo|form|parse_form(form)end# or if you want to skip the mechanize part and know the form key...Googletastic::Form.new(:form_key=>"abc123",:title=>"What do you think of the Google Form API")parse_form(form)

Then just build an ERB/HAML view of it, add some validations, style it, and it looks like you've built a survey system from scratch.