graphicpush

Thoughts on branding, design, writing and life by Kevin Potts. Established 2003.

Using Local Storage as a Light Database for Form Submissions

Local storage, a major component of the HTML5 revolution, has made saving data to the user’s device/browser much easier. In this example, we’re going to use jQuery to convert form information to JSON, save it to a unique record in local storage, and then read all records back out in a table.

In building a web app for my company recently, I needed a way to store data locally when the user was offline. Cookies were not going to work for two reasons: I needed a more intelligent, relational model, and I did not need to rely on a connection to a server. Web SQL databases were also on the table, but with the spec no longer being maintained, I wanted something a bit more future-proof. Also, I did not need the firepower of SQL — I wanted to store a basic array of information using keys and values.

So local storage, which is a widely supported component of HTML5 (including iOS which was the target medium for the app) seemed the best route to take. A connection to the server is irrelevant, you can store up to 5MB before the browser displays a grouchy warning, and the API was dead simple to integrate into the jQuery framework I was already building.

Local Storage Basics

Using the API is as simple as calling a few lines of JavaScript. To set the value of an item, use the setItem() method:

 localStorage.setItem('blindtastetestresult','pepsi');
 

To call the value back, use the getItem() method:

 localStorage.getItem('blindtastetestresult');
 // returns "pepsi"
 

To remove a value, use the removeItem() method:

 localStorage.removeItem('blindtastetestresult');
 var result = localStorage.getItem('blindtastetestresult');
 // value of "result" is null
 

To indiscriminately destroy everything in local storage:

 localStorage.clear();
 

That’s pretty much the API in a nutshell. There is one big caveat: local storage only stores strings, so objects don’t work. In order to get around this, you must stringify the objects before they’re stored, and parse back out when they’re called. Thank the Interweb Gods for JSON as it’s ideal for this. We’ll cover that shortly. (This behavior is further explained on Apple’s Developer Library.)

Saving Form Values in Local Storage

Here’s the fun part. Let’s say we have a basic form whose results we want to store locally. Whether they’re ultimately passed to a server is irrelevant. For now, we just want to grab the field values and create a new “entry” into our lame psuedo database.

Here’s our very basic form:

 

Create a Unique ID

In order to associate each new submission with a unique ID, we need to first create the ID. We’ll reserve lastID for this.

 // grab the next available ID from storage
 var lastID = localStorage.getItem( 'lastID' );

 // if the local storage comes up empty, reset count to zero
 function initializeID() {
   var lastID = localStorage.getItem( 'lastID' );
   if ( lastID == null ) {
     location.reload(true);
     localStorage.setItem( 'lastID',0 );
   }
 }

 initializeID();
 

We cannot have a null value for the ID when starting the app, because a null value cannot be incremented. This is only an issue at the onset. So we test for null, and if true, assign it a value of zero. From then out, until the local storage data is destroyed, we’re good to go.

Saving the Form Data

Using our very bestest friend jQuery, we’re going to grab the values of the form, create an object, stringify it into JSON with the ID incorporated, save to local storage, increment the ID, and then reload the form. Here is the function I created to do this, which could probably be improved upon if there are any true JavaScript/jQuery experts willing to help. (Note I used the jQuery form validation plugin in my project, so I left it here for posterity. This is not required, naturally.)

 // before the function, create a uniqueID variable
 var uniqueID = lastID;

 $("#save").validate({
   // using the jQuery Validate plugin here
   invalidHandler: function() {
     alert("Please fill in all of the information.");
   },
   // continue doing stuff if all fields validate
   submitHandler: function(form) {
     // get input field vars
     var fname = $('input[name="fname"]').val();
         lname = $('input[name="lname"]').val();
         email = $('input[name="email"]').val();
         somethingsecret = $('input[name="somethingsecret"]').val();
     // convert the ID to a string so it can be used as a key
     var formSaveID = uniqueID.toString();
     // assign everything to an object for local storage		
     var newFormSave = {};
         newFormSave.id = uniqueID;
         newFormSave.fname = fname;
         newFormSave.lname = lname;
         newFormSave.email = email;
         newFormSave.somethingsecret = somethingsecret;
     // turn data into JSON string
     localStorage.setItem( formSaveID, JSON.stringify( newFormSave ) );
     // reload history with new entry; see below
     loadHistory(uniqueID);
     // increment the ID
     uniqueID++;
     // save next available ID (note: bugs/features in iOS demand
     // the key be removed first, and then reset; a straight
     // overwrite using the same key does not work)
     localStorage.removeItem( 'lastID' );
     localStorage.setItem( 'lastID', uniqueID );
     // optional; this part clears all of the values in the form		
     $form.find('input[type="text"],input[type="email"]').val("");
     // again optional; this is where you could actually post via Ajax
     $("#save").ajaxSubmit();
     return false;
   }
 });
 

Once this function completes, a new key is created with a new ID, and the associated value is a short bit of JSON holding the aggregate form information.

Reading the Information Back

All of this is useless unless you’re going to take advantage of the data. The simplest example is to read the records back as a table to quickly view a history of form submissions, so that’s what we’ll cover.

You’ll notice in the HTML above, we had an empty table. You will also have noticed that in the form submission function above we called the loadHistory() function before we incremented. (The timing of this is important. In its current location, the function will load all records, but if we loaded the history after we incremented, then the last record would be blank because there would be no value yet for that new ID. God I hope that makes sense.)

For this example, we’re going to use the galactically awesome jQuery JSON plugin, which simplifies the parsing of JSON data to the point where even morons like me can get it right.

 function loadHistory(uniqueID) {
   // clear table and append table header row
   $("table#history").html("").append(
     "<tr><th>First Name</th>
     <th>Last Name</th>
     <th>E-mail</th></tr>");
   // for the loop, start at the beginning
   ID = 0;
   // the loop count is the same as the last unique ID;
   // if the last record saved had the ID of 57, then
   // there are 57 records to display
   while (ID<=uniqueID) {
     ID.toString();
     // pull the JSON string for the current ID in the loop 
     // and extract data into variables
     var history = localStorage.getItem(ID);
         fname = $.evalJSON(history).fname;
         lname = $.evalJSON(history).lname;
         email = $.evalJSON(history).email;
     // render a row of data for each record
     $("table#history").append(
       '<tr class="' + ID + '">
       <td>' + fname + '</td>
       <td>' + lname + '</td>
       <td>' + email + '</td>
       </tr>');
     ID++;
   } // ends loop
 }
 

And that’s it. Every time the form is submitted, the history table gets refreshed. Are we doing a lot of DOM manipulation? Yes. Could there be a far more efficient way of doing this? Probably. Does it really matter if we’re only talking about local storage, a relative experience, instead of an enterprise-grade application? Probably not.

Comments and Extensibility

This is a fairly nuanced example of local storage, but it served my purposes very well and has been tested heavily in the field with much success. Hopefully some of the techniques demonstrated can help you in your project. Some ideas for extending this project:

  • Applying a dynamically generated timestamp to each record.
  • Building a function to call up one specific record based on one of the form values, or a rough search feature.
  • Having a callback from the Ajax script to determine whether the actual server-side submission was successful, and if it failed, a button to resubmit the data that was already captured.

If you see opportunities for optimization or overall performance improvement, please let me know.

, , , , , ,

commentary + criticism

Jireck

wrote the following on Wednesday April 6, 2011

Hello,

A really great Thanks for this article….

Steve

wrote the following on Thursday April 7, 2011

Any chance you could give the code sections more contrast? It’s a strain to read & it seems worth reading :-)

Liam Goodacre

wrote the following on Monday April 11, 2011

Have you made a mistake in the code snippet for ‘Saving the Form Data’?

// …
// assign everything to an object for local storage
var newRegistrant = {}; newFormSave.id = uniqueID; // …

newRegistrant isn’t used anywhere, was it renamed to newFormSave but the initial one was missed?

Liam.

Kevin

wrote the following on Monday April 11, 2011

@Steve — Do you not see the code formatting? Or is it all in black?

@Liam — You are correct. Mistake fixed. Thank you.

Riyadh

wrote the following on Tuesday April 12, 2011

Nice article. Now I don’t need to fire up my virtual server to test websites :)

Dalibor Simacek

wrote the following on Monday April 25, 2011

Very nice and simple tutorial, thanks!