Dienstag, 20. September 2011

localy.js: Client-Side-Storage made easy


Introduction
In my last post I talked about the different ways of how you can implement Client-Side-Storage in your Webapplication. In my Conclusion I summarized that Webstorage is the only technique that is supported my all major browser, but that it only offers the storeage of simple key-value-pairs.

I believe there is a lot of potential in Webstorage that could be unveiled, if the API for using it would be somewhat more convient. To be more concret: I believe developers would more likely to save and retrieve objects, than to save and retrieve key-value-pairs. Because of that I developed the small Javascript-Framework  "localy.js". It is a lightweighted wrapper around Webstorage, which makes Client-Side-Storage more convenient.

How to use it
It has no depencies whatsover. Just include this at the end of your webpage (but obviously before the Code, that relies on this Framework):

<script type="text/javascript" src="/js/storage.js"></script>

Now, imagine you want to store a Student-Object:


 // create and save a new Student
  var student = new LocalyObject("Student");
  student .firstname = "Peter";
  student.lastname = "Pan";
  student.age = 12;
  student.save();


If you want to retrieve all Students, just do this:


var localy = new Localy();
var students = localy.getObjects('Student');
for (var i = 0; i < students.length; i++) {
 var student = students[i];
console.log(student.firstname + " " + student.lastname + " is " + student.age + " years old!");
}


How does it work
storage.js uses Webstorage to save all objects in a similiar way on how the Entity-Attribute-Value-Model works like. So the Student-Object in the example above is saved with the following key-value-pairs:

"Student" => "exists"
"Student_properties" => "name||age"

"Student_1_firstname" => "Peter"
"Student_1_lastname" => "Pan"
"Student_1_age" => "12"

The first two key-value-pairs are for describing the "Class" Student itself and are just stored once for each different class.



Use it and give me Feedback
It has no depencies whatsover, and it is licensed under the MIT-License. You can find it at Github https://github.com/paskster/localy.js. If you use it, please give me Feedback. Especially Cross-Browser-Issues, other Bug-Reports and Suggestions to enhance this Framework are very welcome.


Furter Enhancements
Therere are several shortcomings that I will work on in the next couple of weeks:
1.) Keep track of the type of the properties. Like:
"Student_name" => "String"; 
"Student_age" => "Number"
2.) If an objects contains other objects, save them all and keep track of them. Like:
"Student_1_Teacher" => "Teacher_2"; 
"Student_Teacher" => "Object";
3.) Save functions as well. Like:
"Student_getAge" => "function"; 
"Student_getAge_implementation" => "return this.firstname + ' ' + this.lastname";
4.) Reduce the global Footprint and make in even easier to use. Right now we have two global variables "Localy" and "LocalyObject". I will work on that.

Montag, 19. September 2011

Client-Side Storage

Introduction
With the growing interest of HTML 5, there also a lot of new concepts arising that specifically deal with client-side Storage for your Webapplication. Especially for Mobile Applications Client-Side-Storage can mean a big difference for the User Experience. In this post I would like to give an overview of existing techniques for Client-Side-Storage and specifically focus on Webstorage (sometimes called Dom Storage), which is the only one that is supported by all major browsers.


SQL-like storage
Relational Databases and SQL as a Query Language to retrieve that data, is the major concept of how persistence is implemented on most Webservers. Some database-implementations and their SQL-dialects have already made it to embedded systems, like SQLite on Android and iPhone. So it is pretty straightforwared to use the same technology for client-side-storage. There are two different techniques of interest:
  • Web SQL Database
  • Indexed DB
Web SQL Database was once on the  W3C Recommendation track and browsers like Chrome, Opera and Safari begann supporting it. But the team of Mozilla Firefox refused to support it, mainly because they felt it being to closely related to SQLite and not offering enough fexibility. So the W3C stopped working on it, and  it will propably be completely replaced by Indexed DB.

Indexed DB is right now a working draft of the W3C but intended to be a W3C recommendation at some point in the future. It offers an somewhat SQLite-like syntax to store and retrieve keys and their corresponding values. Its implemenation by the browser consists of an persistent B-tree data structure.
Indexed DB however is still very premature. Only Firefox 4 beta and Chrome 11 canaray are supporting it. Here is a Javascript-Shnippet that shows how you would store data inside it on your Client-Side:


var kids = [
  { name: "Anna" },
  { name: "Betty" },
  { name: "Christine" }
];
 
var request = window.indexedDB.open("CandyDB",
                                    "My candy store database");
request.onsuccess = function(event) {
  var objectStore = event.result.objectStore("kids");
  for (var index = 0; index < kids.length; index++) {
    var kid = kids[index];
    objectStore.add(kid).onsuccess = function(event) {
      document.getElementById("display").textContent =
        "Saved record for " + kid.name + " with id " + event.result;
    };
  }
};

(here is the related Article with more information)

Webstorage
Webstorage (or DOM storage) is the only technique that is supported by newer versions of all major browsers: IE 8+, Firefox 2+, Safari 4+, Chrome 4+ and Operage 10.5+. It only offers storing pairs of keys and their corresponding values. There are two different ways on how to store thes key-value-pairs:
  • sessionStorage: this objects saves the data only for the very same session. Different browsers have different understandings on what a session actually is. But you can usually count on the data as long as the user hasn't closed the browser-application
  • locationStorage: this objects saves the data for as long as possible. Browsers obviously offer a way for the user to delete stored data. But you could normally count on the data being available for pretty much ever; as long as it doesn't exceed 5 Megabytes for your domain.
You can take a look at my project myhealth.reeple.net where I prototyped an application using webstorage. The interesing part is, that you only have to open the url once and can afterwards navigate through the application without the necessity of further internet connectivity. This is an example on how I store a new entry a User just "posted" to his Client-Side-Storage:



$("#submit_button").click(function()  {
  // get the values
  var name = $('#input_name').val();
  var date = $('#input_date').val();
  var description = $('#input_description').val();
   
  // check how many objects we already have
  var num = localStorage['num'];
  if (num == null || num == undefined) {
   numEat = 0;
  }
  
  // save the new object
  localStorage[ num + '_name' ] = name;
  localStorage[ num + '_date' ] = date;
  localStorage[ num + '_description' ] = description;
// increase the counter var numNew = parseFloat(num) + 1; localStorage['num'] = numNew;
});


The solution is a little bit rough around the edges, because I have to keep track of how many objects I have stored myself. And right now it only supports on type of objects. But  you see how it would work.


Conclusion
There are three basics that you should keep in mind after reading this post:
  1. Right now only Webstorage (= DOM Storage) is at least supported by the newer Versions of all major browsers. It only offers Key-Value storage and retrieval and only up to 5 MB.
  2. Web SQL Database was once on the W3C Recommendation track, but the specification work has stopped. It is still supported by some versions of Chrome, Opera and Safari. 
  3. In the future Indexed DB (= WebSimpleDB or Indexed Database API) will probably gain a lot of momentum, but it is right now only supported by Firefox 4 beta and Chrome 11 canary. 
For further reading, check out:

Dienstag, 13. September 2011

Understanding Links, URLs and Pages in jQueryMobile

Disclaimer: In this post I am referencing the third beta-release of jQueryMobile, internally called "1.0b3", that was released 5 days ago. Future Versions may work differently.

Introduction
Imagine you have a standard website at www.myWebsite.com. If you are adding a link on your website that is referring to another page of the same website, you add something like this <a href="/myInternalUrl">myLink</a>. No matter on what current page you are at the moment, your Browser will make an synchronous HTTP-Get Request that fetches the page at the given Url and opens this page in your browser.

Behaviour with jQueryMobile
With jQueryMobile this is a whole lot different! In your sourcecode you can also add links like so: <a href="/myInternalUrl">myLink</a>. However instead of making a synchronous call, jQueryMobile automatically handles the onClick-Event on that link and will trigger an Ajax-Request, that loads the data. This has two advantages:
  • The UI stays responsive
  • jQueryMobile has the chance to process the newly loaded data, before it is shown to the user
In fact this feature is one of the most integral parts of jQueryMobile and the hardest do understand. So let us dig depper.

Loading a Page
Every content in jQueryMobile is supposed to be embedded into a page. As you can see here a page is nothing more than a div, which has the HTML 5 attribute data-role="page". This div basically contains three more divs, one with the data-role="header", on with data-role="content" and the last with data-role="footer". This is obviously similar to the concept of how most of the Views in iPhone application look like.

A single HTML-Document however may contain more than a single page. This has the advantage that the client only needs to do a single HTTP-Request to load several pages, and he doesnt need to wait again when he naivgates between these pages. The number of pages you want to embedd in a single HTML-Document is obviously limited by the amount of time you want your users to wait for the first page.

If the user clicks on a link within your page, one of the following applies:
  • The page that is represented by that link was already loaded. jQueryMobile can therefore show that page right away.
  • The page needs to be loaded from the Server. jQueryMobile will then show a loading screen and load this data asynchronously via an Ajax-Request.
But how does jQueryMobile know, if the requested page that is represented by the link, was already loaded or needs to be loaded? To understand this, we need to look at the attribute data-url and id of pages.

Data-Url and Id of Pages
The div that represents a page can, like every other HTML-Tag, contain an id. If the page has a unique Id, you can use that to point to the page, instead of using the Url. For example a click on <a href="#impressum">impressum</a> will trigger jQueryMobile to look for a previously loaded page, whose id is "impressum". If no such page is found, an error will be thrown.

The Url that is displayed to the user in the browser bar when he is on that page will be www.myWebsite.com/previousUrl#impressum. This means that the Url shown in the browser bar actually depends on the Url of the page, where you clicked on that link.

Fortunatly jQueryMobile offers a workaround. By using the attribute data-url inside your page-div you can specify the Url that should be displayed for this page. For Example just add data-url="/mobile/impressum" as an attribute to the impressum page, and the Url that is shown in the browser bar will always display www.myWebsite.com/mobile/impressum for this page.

Because the Id of that page hasn't changed, you can still reference to that page via <a href="#impressum">impressum</a>. Even the link <a href="/mobile/impressum">impressum</a> will point to the very same page. You should always be sure to use the latter, if you don't know if the client has already loaded the page. 



Please will free to give Feedback on my Post! I am looking forward to any comments!