-
Notifications
You must be signed in to change notification settings - Fork 152
Local storage (The M Project 1.x)
Data Provider serve as an abstraction layer to several different storage techniques for the model. Every model needs to define which data provider to use. The data provider itself implements the way to store a model. The model simply tells its data provider to save itself.
Previous to the LocalStorage, web applications did not have direct access for storage mechanisms beneath something that was invented in the early days of the web: cookies. With cookies it is possible to store some key/value pairs with the browser. The downsides of cookies are, beneath others, their limited size (only up to 5k) and the fact, that they are sent with every request (unencrypted as long as not the whole page is SSL encrypted) and so potentially slow down a web application. What we want is (c.p. http://diveintohtml5.org/storage.html):
- a lot of storage space
- on the client
- that persists beyond a page refresh
- and isn’t transmitted to the server
And here we are: HTML5 LocalStorage. HTML5 LocalStorage is part of a specification by the W3C, named WebStorage. The specification names to storage mechanisms:
- SessionStorage: storing key/value pairs for the duration of a session
- LocalStorage: storing key/value pairs for an undefined timed, at most: as long as the LocalStorage is not cleared explicitly, means as long as the browser is not deleted from disk.
We want to focus on LocalStorage here. As mentioned above, the local store takes a value and saves it under a certain key. The key is a string, the value can be of any type that JavaScript supports, but is as well saved as a string.
localStorage.setItem("counter", 100)
As we can see in the chrome developer tools, the value is saved in local storage with the key "counter".
We can now retrieve this value from LocalStorage:
localStorage.getItem("counter")
There's some more functionality available, e.g. for returning all keys, but in the main, this is it, this is LocalStorage.
Now when developing an application with the M project that has one or more models, you might not want to work with the localStorage
object directly everytime (even though you can!). Isn't it much more comfortable to simply call something like save
on the model object and the rest is done automatically? This is where the data providers come in.
Each Data Provider basically implements three methods:
-
save
: persists a model into storage -
find
: finds one or more models based on a constraint -
del
: deletes a model from storage
These methods are called by the model which itself implements these methods as interface to the data providers.
In the model's "blue print" we define which provider we would like to use: we simply name it as the second parameter to the model's create method, just after the properties object.
Contacts.Contact = M.Model.create({
__name__ : 'Contact',
firstName: M.Model.attr('String', {
isRequired:YES,
});
}, M.DataProviderLocalStorage); // here we simply name it as the second parameter to the create method.
Every model object that is now instantiated on basis of this blue print has now the ability to save itself into local storage. Because models hold the application's business data, we now have a powerful option to do things like offline usage or caching in our application.
When instantiating model records of the above created model we have LocalStorage support on board. It's very simple to save a model now. Take this simple example that we could use in a controller:
// adding a new contact
var c = Contacts.Contact.createRecord({ firstName: 'Johnny' });
c.save();
We then find the model, after the successfully passed validation, in LocalStorage under the key build based on the model's name (e.g. "Contact"), an underscore and its internal id. Note: This internal id must not be mixed up with the ID that is used in RDBMS storage, e.g. in our WebSQL provider. We might want to refer to the internal id as m_id.
As we see, the model is dumped into a JSON string and then saved.
We can retrieve one or more models from LocalStorage with the find
method. While we're calling save
and del
on a concrete model record we call find on the model itself, here Contacts.Contact
(means the Contact
model of an app named Contacts).
There are three possible calls:
-
find()
: without parameters: will return all models records for a model -
find(constraint)
: with a constraint filtering the result -
find(key)
: with a certain key that returns the one model record that's saved with the key, if key is an existing key.
Above, we said that the model is saved as JSON string. But we are not getting any string as return value, the model string is directly transformed into JavaScript objects again in find
. We don't have to take care of that.
var c1 = Contacts.Contact.createRecord({ firstName: 'Paul' });
var c2 = Contacts.Contact.createRecord({ firstName: 'Peter' });
var c3 = Contacts.Contact.createRecord({ firstName: 'Pierce' });
c1.save();
c2.save();
c3.save();
var contacts = Contacts.Contact.find(); // returns all saved models when no parameter is given
=> [Object, Object, Object, Object] // we get 4 contacts, because we saved 'Johnny' before...
Simply calling find
will return all model records for a model. But we can also pass a constraint that is applied to all models and filters the result. Examples for constraints are:
- firstName = "Johnny"
- price > 12.50
- *status != "booked"
It is only possible to pass one of these queries: and
or or
conjunctions are not possible. The result of this find is an array with the results. If no model matches the query the array has a length of 0
.
All we got to do is, to pass an object with the property query
and the query itself as value to the find
call:
var allPeters = Contacts.Contact.find({
query : 'firstName = "Peter"' // returns all models records with "Peter" as firstName property value
});
=> [Object]
var allButPeter = Contacts.Contact.find({
query: 'firstName != "Peter"'
});
=> [Object, Object, Object]
If for some reason you know the specific or construct it by the model's internal id and name, e.g. Contact_123
, you can pass this key to the find call as part of the parameter object, too. Instead of passing the object with a query
property, you pass it with a key
property and the key as value. This returns exactly one model record (if the key exists), not an array!
var theOneContact = Contacts.Contact.find({
key: 'Contact_123'
})
=> Object
Deleting a model from LocalStorage is as simple as saving it:
// deleting a new contact
var contacts = Contacts.Contact.find(); // returns all contact models
for(var i in contacts) { // delete every contact
contacts[i].del();
}
Executing this code snippet in our Contacts app would delete all contact model records saved in LocalStorage.