[3.2] Ext.ux.PersistentStore: auto-save records to HTML5 DOM storage

[3.2] Ext.ux.PersistentStore: auto-save records to HTML5 DOM storage

I desperately wanted the ability to have a Ext Store that fetches data from a server, but can cache that data on the client so it persists across page reloads. This improves performance and reduces demand on the server providing the data. The problem is Ext's current data architecture can't do a lot of the things that are needed to persist Record objects properly, so this UX has several pieces to it to enhance Ext's architecture. Hopefully some if not all of these enhancements will make it into the main distribution, so I'm including lots of the justification for the design.

The first problem is that although DataReaders support using convert functions to turn raw data into objects (like Date), there is no way to do the reverse and turn an object back into a serialized form that could be re-read by the same convert function later. As an example of this limitation, if you attempt to build a store with a date field and attach a JsonWriter to it, the JsonWriter will send the date back in the default JavaScript format. Putting custom classes on Records gets even messier!

The solution to this is to add another custom function to data types that does the inverse operation of convert, which I call write(). Here's the enhancement to the DATE type that makes this work (note I've made some other enhancements here that rely on Ext.ux.DateFormat; it would be rather trivial to write different versions of these to use the builtin Date.parse() and Date.format() functions):

Once we have a write() function to call, something needs to be able to call it! My first attempt at this was an enhancement of DataWriter.toHash(), but relying on DataReaders and DataWriters for Record serialization and deserialization had a bunch of problems:

A Store may or may not have a DataWriter defined on it

A Store's DataReader may be expecting a format that can't be persisted to DOM storage (XML)

DataReaders and DataWriters are designed to be attached to Stores, and Stores are coded to only interact with a single DataReader and DataWriter combination.

To solve these problems I chose instead to enhance the Record class itself, so a Record can produce a raw encodeable object representation of its data which can then be read back in to rebuild the original record. This is the equivalent of implementing Serializable in Java.

As an aside, we can now easily fix that bug in DataWriter.toHash() that causes Dates to be sent back to the server in the default Javascript format. It might also be possible to refactor DataReader or JsonReader to use deserialize as part of its read operations.

Code:

Ext.override(Ext.data.DataWriter, {
toHash: function(rec, config) {
var data = rec.serialize(true, this.writeAllFields).data;
// we don't want to write Ext auto-generated id to hash. Careful not to remove it on Models not having auto-increment pk though.
// We can tell its not auto-increment if the user defined a DataReader field for it *and* that field's value is non-empty.
// we could also do a RegExp here for the Ext.data.Record AUTO_ID prefix.
if (rec.phantom) {
if (rec.fields.containsKey(this.meta.idProperty) && Ext.isEmpty(rec.data[this.meta.idProperty])) {
delete data[this.meta.idProperty];
}
} else {
data[this.meta.idProperty] = rec.id
}
return data;
}
});

Once all those framework changes are made, declaring Ext.ux.PersistentStore is pretty easy: