Goodbye Save Button

No Floppy Disk, slashed circle with blue 3.5" floppy diskI’ve always been amazed at web applications such as Asana, where in this case, you’re creating a project in a notepad fashion, not really viewing all the ugly squares on an underlying rigidly structured form.  You click, or press a shortcut key and you’re presented with a box in which to type, press enter, and that one magically goes away and another appears, leaving your text in its place.  Clicking intuitively somewhere else reveals a text area for a longer description, or clicking the sub-tasks icon, presents you with a fill-as-you go checklist.

Its nothing short of brilliant, and the best thing – there is no SAVE button.

I want this functionality for some longer forms I’m working on, think college undergrad and graduate applications.

Currently, you have to click save to save whatever you’ve entered and that feels sloppy, yes, its typical and expected, but I’d like something better and different, I like my apps to stand out. Plus, some of these pages have a lot of fields (for a reason) and if anything happens to your connection, the browser or your session, you’ll lose  that data.  If that happens, even if its not your application / server’s fault, you still leave a bad taste in the user’s mouth, and they may not be willing to fill it out again.

I set out on what I figured would be an R&D quest to figure this out, it ended up taking me less than 30 minutes to get a proof of concept working using my favorite interactive framework,  jQuery, and that included creating a MySQL database to save data to 🙂  I used plain old jQuery – if you can call it that.  I didn’t add in any extensions of modules to give me this ability.

Looking at my solution, it is so incredibly simple, its almost stupid.  I liken it to figuring out a magic trick, and thinking, really that’s all there is to it?  That impressed me?  I’m sure there are many other ways to do this, possibly even better, there might even be a module or extension available, but this is how I went about it.

I created a form with both a hidden and an input field that looked like this :

<input type='hidden' id='r_id' value='' />
<input type="text" class='blursave' placeholder="Type something…" 
  id='field_label' d_name='field_label' d_mod='z_test' />

 

The r_id field will hold the database record’s primary key (if we’re editing a record), and will be empty if this is a new / first time view.

The real key here is the blursave class – this can be called anything, I made that up, the name is arbitrary, but having a unique class name assigned to your fields is very important.  I’m also using Bootstrap which is not required to do this and is completely optional. I used it because I like my stuff to look nice even when I’m just “playing around”, so you’ll notice the placeholder attribute here as well, if you’re not using bootstrap – you can remove this.

Of almost equal importance is the d_name, and d_mod attributes.  The d_name is the field_name in the database where this value needs to be… or came from.  With my prototype (I don’t necessarily recommend this for production) I also added d_mod – the table this value belongs to.  For obvious reasons you don’t want to expose this to the world.  You could easily encrypt these values to make them a little less hackable, and I will do that when this ends up in production, but for my prototype, I didn’t want to spend the time doing that.  Having the table name with the data element makes is easier for a complex form that may be joined in the database schema.  You could add other attributes if you wanted to keep track of the record’s foreign key.  You could also combine all of these important bits into one field, encrypt it and just send one bit of data, decrypt and process it on the server.  Maybe my next post will have all that in there, we’ll see.

Continuing on,  the data we have represented in our field’s attributes consist of :

  • primary key of the record (or not)
  • database field name
  • database table name
  • the value going into the database

Here’s where the magic comes in.  Using jQuery, I can add a blur action to the .blursave class, so anytime someone blurs (moves focus) off of a field, this function is called.  It’ll look a lot like this –

$('.blursave').blur(function() {
});

So now what?  When this gets called because someone moved away from our field, we need to get those bits of information, then assemble arguments to post to a page that will save that information for us.  We’ll use the traditional div selector (#) as well as the .attr() method to grab our custom attributes like this –

$('.blursave').blur(function() {
   var r_id = $('#r_id').val();
   var d_val = $(this).val();
   var d_name = $(this).attr('d_name');
   var d_eid = $(this).attr('d_eid');
   var d_mod = $(this).attr('d_mod');
  $.post( "update.php", { r_id:r_id, d_val:d_val, d_name:d_name, d_mod:d_mod })
     .done(function( data ) {
         obj = JSON.parse(data);
         console.log( obj.sql);
         console.log( obj.rid);
         var rid = obj.rid;
         $('#r_id').val(rid);
     });
 });

You can see that I’ve accessed the record id (r_id) directly.  If this is a new form, this is empty.  If someone were coming back to edit data, this would have a value set (more on that in a second).  I then get the value typed into our field – .val() , and then use the attribute selector to snag the various other bits of data we’ll need to make our database call.

Once I have that data, I use the .post method to send this data to my update.php page.  The parameter section may look confusing, but those are key:value pairs, I just happened to name my keys the same as my variables.  This is probably bad practice, but whatever, its a prototype.

Over to update.php

In our update.php script, I set my php variables (again not a best practice, you’ll want to strip bad characters, and prevent hacking, yada, yada, yada)

$rid = $_POST['r_id'];               # record id to update (if exists)
 $dval = $_POST['d_val'];            # value to insert/update
 $dname = $_POST['d_name'];          # database field name to update
 $dmod = $_POST['d_mod'];            # tablename to update

I need to determine if this is a new record, meaning its the first data point entered on the form, in which case I need to run an insert, or if this is the 2nd+ data point, which then requires an update.  In the first situation – $rid would be empty, right?  Yep –

if (empty($rid)) {
   $sql = "insert into " . $dmod . "(".$dname.") VALUES ('".$dval."')";
   $res = $db->query($sql);
   $rid = $db->insert_id;
} else {
   $sql = "update " . $dmod . " set " . $dname . " = '" . $dval . "' where id = " . $rid;
   $res = $db->query($sql);
}

Agreed, this is not the best way to deal with checking $rid, but will work for us now.  If that is empty – which it would be on the first field used on the form, we’ll construct an insert statement, execute the query, and get the incremented primary key ($db->insert_id).  If it is not empty, we know which record to update, and we’ll construct an update statement as above and execute that.

SIDENOTE: depending on the database you’re using, you could probably use REPLACE INTO or UPSERT or whatever your database may support.  This method is guaranteed to work for any database that supports insert and update 🙂

After one of the above statements executes we have an $rid set, whether its the original we came to the party with, or one we picked up with our insert.

Again, since this was a prototype (no flames please), I wanted to see what the database was doing, so I included the SQL statement  in my return JSON string <G>.   I set up an array, and then json_encoded it to return back to my jQuery on my form page

$return_array = array ('sql'=>$sql,'rid'=>$rid);
print json_encode($return_array);

 

To the Form Code Batman

In our .post we have a status called .done – that executes … well… after the post is done, makes sense. The json string we created in our PHP file is returned to us and a callback function is executed, and the response (our json string) is stored in a javascript variable called data (you can name this anything), and it looks like this :

{"sql":"insert into z_test(field_name) VALUES ('this')","rid":175}

in order to use the json data, we need to parse it

obj = JSON.parse(data);

now we can access the elements of the data like this :  obj.sql  and obj.rid

I log these to the console so I can see what it going on (again for production, or on a live server even for testing,  you probably wouldn’t send the SQL statement, and wouldn’t log to the console).  Since this is running on my laptop, I don’t care.

console.log(obj.sql);
console.log(obj.rid);

then I set a javascript variable called rid to the record id we just touched in update.php (“rid” from the json string), and update the hidden r_id on our form.

var rid = obj.rid;
 $('#r_id').val(rid);

If this were a new form, next time a field is blurred, we have the record id of the first inserted record in our database and our update.php file will update and that data will be added to that record.  If we’re already editing a record, and this r_id was set before our update.php was run, eh, the code updates #r_id value anyway.  If you wanted you could check to see if its empty first then updated it, its up to you.

So there you have it, it took me longer to write this up than to actually build this save-on-blur prototype.

Quickly thinking about it, you’ll need some other code to deal with radio and checkboxes, but its still the same concept.

If anyone has a better way to do this please share, I’d love to hear about it.

 

This entry was posted in jQuery, PHP. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *