Nov28
An Interesting Challenge
- posted by: Casey O'Hara
- 2 comments
- post a comment
Last week we had a client call us with an interesting problem. They have a tradeshow kiosk they’d like to use to capture form data from users and collect it at the end of each day.
The challenge: the kiosk doesn’t have an internet connection and can’t run a webserver. It can display webpages, but only through an IE8 iframe.
If the kiosk had a net connection we’d build a static form to post the data to a remote webservice. But that’s out. If the kiosk could run a webserver we’d build a simple webapp to handle the data locally. But that’s out, too.
We decided to solve the problem by saving the data to localStorage. localStorage is a key-value store built into most modern browsers, and a perfect mechanism for persisting simple form data. It has a minimal JavaScript API (setters and getters) via the window’s localStorage object that makes it really easy to work with.
Here’s how we did it:
We started by setting up a simple Storage class to interface with localStorage. This isn’t entirely necessary, but makes it easier to save and retrieve the data without having to parse/stringify on every transaction. (Yes, we love CoffeeScript.)
class Storage
constructor: (@name)->
@data = []
# Check to see if the object already
# exists in localStorage
# If it does, grab the value
# Otherwise, create a key in localStorage
# with an empty array of data
if localStorage[@name]?
do @fetch
else
# We need to stringify the data as JSON
# because it is stored as a string
localStorage.setItem(@name, JSON.stringify(@data))
# Pull the data out of localStorage and populate
# the instance's @data array property
# We need to parse the data as JSON
# because it is stored as a string
fetch: ->
@data = JSON.parse(localStorage.getItem(@name))
# Add an item to the instance's @data array and
# re-save to localStorage
add: (data) ->
@data.push data
localStorage.setItem(@name, JSON.stringify(@data))
Next, we set up a Form class to observe the form on the page and do some simple validation before finally committing it to a storage instance.
class Form
constructor: (@$form, name)->
@storage = new Storage(name)
@$fields = $('input, textarea, select', @$form)
@$form.submit (event) =>
event.preventDefault()
if @validate()
do @save
do @clear
save: ->
data = {}
@$fields.each ->
name = $(this).attr('name')
value = $(this).val()
data[name] = value
@storage.add data
clear: ->
@$fields.each ->
$field = $(this)
type = $field.attr('type')
$field.val('') if type is 'text'
validate: ->
data = {}
valid = true
# Simple 'required' validation
# Loop over each of the form fields
# If the field is required, then check to see if it is blank
@$fields.each ->
$field = $(this)
value = $field.val()
required = $field.hasClass('required')
if required and value is ''
valid = false
$field.addClass('invalid')
else
$field.removeClass('invalid')
return valid
The client needed a means of retrieving the data, so we set up way to grab all of the data and render out a table with each of the entries.
class ResultsTable
constructor: (@$table, name) ->
@storage = new Storage(name)
@results = @storage.data
$head_tr = $('<tr />')
for field, value of @results[0]
$head_tr.append $('<td />').text(field)
$('thead', @$table).append $head_tr
for result in @results
$tr = $('<tr />')
$tr.append $('<td />').text(value) for field, value of result
$('tbody', @$table).append $tr
Finally, an example of how to pull it all together.
On the form page:
<form id="user-form" action="#">
<input type="text" name="first_name" />
<input type="text" name="last_name" />
<button type="submit">Submit</button>
</form>
<script type="text/javascript">
$(document).ready(function(){
new Form($('form#user-form'), 'kiosk');
});
</script>
On the results page:
<table id="results">
</table>
<script type="text/javascript">
$(document).ready(function(){
new ResultsTable($('#results'), 'kiosk');
});
</script>
Given the constraints, we feel like this is great solution. Lucky for us, the browser environment was predictable and IE8 has support for localStorage. All in all, it was a fun experiment with localStorage.


