persisto
Persistent, editable objects for Javascript.
Features
- Persist Javascript objects (
{...}
) to
localStorage
/ sessionStorage
.
Use the get()
/set()
API for direct (even nested) access, hiding the need
to convert from/to JSON. - Cache access to
localStorage
/ sessionStorage
(deferred writing appears to be 10-15 times faster) and remote backends. - Make Javascript objects editable in HTML forms.
Common use case: maintain persistent client settings and let users edit them. - Optionally synchronize the data with a remote endpoint
Overview:
Requirements:
- jQuery
- IE 8+ or any recent major browser
Usage
var store = PersistentObject("mySettings", {
init: {
theme: "default"
}
});
store
now contains the data that was stored in localStorage.mySettings
if
present. Otherwise, store
is initialized to the default values that we
passed with the .init
option.
We can access data using set
, get
, remove
, reset
:
store.get("theme");
store.set("owner", {name: "joe", age: 42});
store.set("owner.role", "manager");
store.get("owner.age");
store.remove("owner.age");
Every modifying operation triggers a deferred commit, so that shortly afterwards
the data is serialized to JSON and written to localStorage.mySettings
.
Synchronize Data with HTML Forms
Form input elements can be synchronized with a PersistentObject
by using two
API calls.
Example:
var settingsStore = PersistentObject("mySettings", {
init: {
nickname: "anonymous",
theme: "default"
}
});
settingsStore.writeToForm("#settingsForm");
$("#settingsForm").submit(function(e){
settingsStore.readFromForm(this);
e.preventDefault();
});
Supported elements are <input>
(type text, checkbox, or radio), <textarea>
,
and <select>
(single and multivalue).
By convention, the html form must use element names that match the data properties.
<form id="settingsForm" action="">
<label>Nickname:<input name="nickname" type="text" value="" /></label><br>
<label>Theme:
<fieldset>
<label> <input name="theme" type="radio" value="default" /> Standard </label><br>
<label> <input name="theme" type="radio" value="light" /> Light </label><br>
<label> <input name="theme" type="radio" value="dark" /> Dark </label>
</fieldset>
</label>
<button type="Submit">Submit</button>
</form>
Note also that only fields are synchronized, that already existed in the storage
data. Use the addNew
option if all form fields should be evaluated and create
new properties in the store object:
settingsStore.readFromForm(this, {
addNew: true
});
Pros and Cons
-
Any PersistentObject
instance is stored as one monolythic JSON string.
Persisto deferres and collates these updates, but modifying a single
property of a large data object still comes with some overhead.
Splitting data into several PersistentObject
s may remedy the problem.
But if your data model is more like a table with hundredth's of rows, a
responsive database backend may be a better choice.
-
Asynchronous operations bear the risk of potential conflicts.
There is currently no builtin support for resolving those.
HOWTOs
Storing Arrays
Arrays are only a special form of plain Javascript objects, so we can store and
access them like this:
var store = PersistentObject("mySettings", {
init: ["a", "b", "c"]
});
store.set("[1]", "b2");
Performance and Direct Access
In general, performance costs of set()
and get()
calls should be
neglectable, compared to the resulting synchronization times, but in some cases
direct access of the internal data object may be preferred.
In this case modifications must be signalled by a call to setDirty()
.
store._data.owner = {name: "joe", age: 42});
store._data.owner.role = "manager";
delete store._data.owner.age;
store.setDirty();
Asynchronous Operation
By default, changed values will be commited to webStorage after a small delay
(see .commitDelay
option). This allows to collate sequences of mutliple changes
into one single write command.
However there are situations, where this is not desirable:
store.set("foo", "bar");
commit();
location.reload();
An alternative would be to disable delay completely by setting commitDelay: 0
.
Synchronize with Remote Endpoints
Optionally, we may specify an endpoint URL that is used to synchronize the data
with a web server using HTTP REST requests (GET and PUT):
var store = PersistentObject("mySettings", {
remote: "persist/settings"
});
$.when(
$.ready,
store.ready
).done(function(){
initPage();
}).fail(function(){
console.error("Error loading persistent objects", arguments);
});
API Reference
Options
The following options are available:
- commitDelay
-
Type:
int
,
default: 500
milliseconds
Commit changes after 0.5 seconds of inactivity.
This means, after each change, we wait 0.5 more seconds for additional changes
to come in, before the actual commit is executed.
The total delay (first change until actual commit) is limited `maxCommitDelay`.
Set to 0
to force synchronous mode.
- debug
-
Type:
int
,
default: 2
Verbosity level: 0:quiet, 1:normal, 2:verbose.
- init
-
Type:
object
,
default: {}
Default value if no data is found in localStorage.
- maxCommitDelay
-
Type:
int
,
default: 3000
milliseconds
Commit changes max. 3 seconds after first change.
- maxPushDelay
-
Type:
int
,
default: 30000
milliseconds
Push commits to remote max. 30 seconds after first change.
- pushDelay
-
Type:
int
,
default: 5000
milliseconds
Push commits to remote after 5 seconds of inactivity.
Set to 0
to force synchronous mode.
- remote
-
Type:
string
,
default: null
URL for GET/PUT request. Pass `null` to disable remote synchronization.
- storage
-
Type:
object
,
default: window.localStorage
Instance of [Web Storage](https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API).
Possible values are `window.localStorage`, `window.sessionStorage`.
Pass `null` to disable persistence.
Methods
Following a list of available methods:
- commit()
-
Write modified data to localStorage.
(Normally there is no need to call this method, since it is triggered internally
after a short collation interval.)
- get(key)
-
Return a data property value (`key` supports dot notation).
- isDirty()
-
Return *true* if there are uncommited or unpushed modifications.
- isReady()
-
Return true if initial pull has completed.
See also the `store.ready` promise, that is resolved accordingly.
- pull()
-
Download data from the cloud, then call `.update()`.
- push()
-
Commit, then upload data into the cloud.
(Normally there is no need to call this method, since it is triggered internally.)
- readFromForm(form, [options])
-
Read data properties from form input elements with the same name.
Supports elements of input (type: text, radio, checkbox), textarea,
and select.
*form* may be a form selector or jQuery object. Example: `"#myForm"`.
*options* is optional and defaults to {addNew: false, coerce: true, trim: true}
.
- remove(key)
-
Delete object property and set the `dirty` flag (`key` supports dot notation).
- reset(newData)
-
Replace data object with a new instance and set the `dirty` flag.
*newData* is optional and defaults to
{}
.
- set(key, value)
-
Modify object property and set the `dirty` flag (`key` supports dot notation).
*value* must be [convertible to JSON](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
- setDirty()
-
Flag object as *modified*, so that commit / push will be scheduled.
- update()
-
Load data from localStorage.
- writeToForm(form)
-
Write data to form elements with the same name.
*form* may be a form selector or jQuery object. Example: `"#myForm"`.
Events
Events may be handled by passing a handler callback option:
store = PersistentObject("mySettings", {
[...]
change: function(hint){
alert("Store " + this + " was changed. Reason: " + hint);
}
});
Note:
Events are not yet finally implemented and subject to change!
This is what we have so far:
{
change: $.noop,
commit: $.noop,
conflict: $.noop,
error: $.noop,
pull: $.noop,
push: $.noop,
ready: PROMISE
update: $.noop
}