While building a native mobile app (via PhoneGap & Sencha Touch 2.1), I wanted to provide an offline fallback mode. After some searching on Google and StackTrace I found a number of posts/guides, but mostly dealing with Sencha Touch 1 and not using the MVC pattern. Here is a complete working example.

1) You’ll need two models

An Online and Offline model. The “fields” property of each model should be identical.

Ext.define('default.model.Online', {
  extend: 'Ext.data.Model',
  config: {
    fields: [
      'name'
    ]
  }
});
Ext.define('default.model.Offline', {
  extend: 'Ext.data.Model',
  config: {
    fields: [
      'name'
    ],
    identifier:'uuid', // needed to avoid console warnings!
    proxy: {
      type: 'localstorage',
      id  : 'news'
    }
  }
});

 

2) Need to create a Store for the Online model

We won’t need to create one for the Offline store, we’ll do that at run time.

Ext.define('default.store.News', {
  extend:'Ext.data.Store',
  config:{
    model:'default.model.Online',
    proxy: {
      timeout: 3000, // How long to wait before going into "Offline" mode, in milliseconds.
      type: 'ajax',
      url: 'http://example.org/data/news.json' // Sample URL that simulates offline mode. Example.org does not allow cross-domain requests so this will always fail
    },
    autoLoad: false // Will load programmatically later
  }
});

 

3) Next, the magic!

Need to update Offline store with Online store details when online. We do this by listening for the Online store to load. Once it has loaded, then we copy all the infromation to the Offline store.

  var onlineStore = Ext.getStore('NewsStore'),
    localStore = Ext.create('Ext.data.Store', { // Create the Offline Store on the fly
      model: "default.model.OfflineNewsModel"
    }),
    me = this;

  localStore.load();

  /*
   * When app is online, store all the records to HTML5 local storage.
   * This will be used as a fallback if app is offline more
   */
  onlineStore.on('refresh', function (store, records) {

    // Get rid of old records, so store can be repopulated with latest details
    localStore.getProxy().clear();

    store.each(function(record) {

      var rec = {
        name : record.data.name + ' (from localStorage)' // in a real app you would not update a real field like this!
      };

      localStore.add(rec);
      localStore.sync(); // The magic! This command persists the records in the store to the browsers localStorage
    });

  });

 

4) Now have to detect if in offline mode.
Easiest way to do this is to see if the OnlineStore failed to load.
IMPORTANT: If the Online store fails to load, it will be necessary to rebind the view(s) to the Offline Store.

/*
   * If app is offline a Proxy exception will be thrown. If that happens then use
   * the fallback / local storage store instead
   */
  onlineStore.getProxy().on('exception', function () {
    me.getNewsList().setStore(localStore); // rebind the view to the local store
    localStore.load(); // This causes the "loading" mask to disappear
    Ext.Msg.alert('Notice', 'You are in offline mode', Ext.emptyFn); //alert the user that they are in offline mode
  });
  onlineStore .load(); // finally try load the Online Store

 

5) Success! But not finished yet!

If you load up your app now, you should see that the localStorage resource now has some entries in it! It is easiest to use Chrome for this.

Lastly if you disconnect your connection and try refresh (assuming you are on localhost), you will see a notification that app is offline, now app falls back to the Offline/LocalStorage store instead.

IMPORTANT: Offline mode will only work after the app has been online at least once before.

Done!

The full source code for this project is available on my GitHub home page. Feel free to fork or send pull requests!

If you have a question or comment please add a comment below.

 


24 Comments » for Sencha Touch 2 LocalStorage example
  1. Riyaad says:

    Hi there,
    I’m following your code on GitHub and wonder if its possible to do this without MVC? Could you kindly assist?
    Thx,
    R

    • Robert says:

      Hi Riyaad, all of the code in the blog post it self can be used directly, without the need for using the ExtJS MVC framework. If you send me your code I’ll take a quick look (pastebin, gist or Github link).

  2. Riyaad says:

    Hi Robert, thank you.
    That would be greatly appreciated.The link is https://github.com/riyaadmiller/LocalStorage, I’m still figuring out GitHub. Only 2 files (index & main) is used. Ignore the app files. I’m currently getting an error on line 73. “Uncaught TypeError: Cannot call method ‘on’ of undefined”.
    Thx,
    R

  3. Riyaad says:

    Hi Robert,

    No worries, I managed to figur it out. The non-MVC based solution is at https://github.com/riyaadmiller/LocalStorage. Hopefully this will help someone out there.

    Thanks again.

  4. Emanuele says:

    Thank you very much, great example.

    It is possible to use this mode to a TreeStore to call a JSONP like this: http://grottaglie24.it/rss/ultime.php?callback=callback1

    How do I recall all that volume of fields?

    • Robert says:

      Hi Emanuele. Yes you can use a TreeStore with JSONP. ExtJS abstracts away details of data provider (i.e. JSONP, Ajax, in Memory, etc…) and provides a single interface to work against.

      I quickly did a simple example showing how to use JSONP with a TreeStore.
      Code: https://github.com/RobK/TreeStoreWithJSONP
      Demo: http://www.robertkehoe.com/demos/TreeStoreWithJSONP/app.html

      Hope this helps!

      • Ben says:

        Great demo. But related Emanuele’s original question: how do you sync the records when they have an arbitrary number of children and or are leaves? Do we need to check if they have children and then recursively populate our record object before syncing? The following just won’t cut it I think:

        var rec = {
        name : record.data.name + ‘ (from localStorage)’ // in a real app you would not update a real field like this!
        };

      • Ben says:

        That actually does work. The problem is the ‘refresh’ event is a bit different with TreeStores since it’s called every time the user clicks an item since it causes the store to be updated. Moreover the event doesn’t return the entire store, only the related sub-list I believe, so it makes it all a bit tricky.

        • Ben says:

          All right I nailed it: Sencha’s built in localStorage framework doesn’t handle nested stores sadly. My end solution was to simply store an entire JSON response from an AJAX call in localStorage myself to then be read into my view on “exception” as follows:

          onlineStore.on('refresh', function (store, records) {
          // Update localStorage var directly
          Ext.Ajax.request({
          url: /* DATA URL */,
          success: function(response, opts) {
          localStorage.StoreJSBlob = response.responseText;
          }
          });
          });

          onlineStore.getProxy().on('exception', function () {
          if (localStorage.StoreJSBlob) {
          var mystore = new Ext.data.TreeStore({
          model: 'MyApp.model.MyModel',
          defaultRootProperty: 'items',
          data: localStorage.StoreJSBlob,
          autoload: false
          });
          me.getView().setStore(questionStore); //rebind the view to the local store
          mystore.load(); // This causes the "loading" mask to disappear
          Ext.Msg.alert('Notice', 'You are in offline mode', Ext.emptyFn); //alert the user that they are in offline mode
          } else {
          Ext.Msg.alert('Error', 'No local data found', Ext.emptyFn);
          }
          });

  5. Emanuele says:

    Thank you very much, but I use Sencha Touch 2, possibly I could use Sencha Architect 2.

    Thanks again, kind regards

    Emanuele

  6. Nocturnal says:

    Hello,

    I downloaded the source code and it works like it should, however when i use this example inside my own code the data is loaded to the local store but after a refresh the store is empty. After populating localeStore, if i console.log(localeStore) the store that works prints _totalCount: 5 while the other store prints _totalCount: 0.

    I know this is very little info but it is the only difference i could find. Do you have any idea of what i could have done wrong?

    • Robert says:

      Hi Nocturnal, can you provide a GitHub or BitBucket link where I can see the code? At a guess, there is something wrong with either the offlineStore setup or something is emptying the localStorage cache. Try adding additional key/values to the localStore, do they persist? If so, then it is almost certainly a setup/config issue.

  7. Nocturnal says:

    Hi Robert, thank you for pointing me toward the key/values of my localStore. I used ‘id’ as one of my fields inside my model, this works fine when i use a database but with localStorage not so much. When i deleted ‘id’ as a field name it worked instantly.

    Thanks you really saved my day!

  8. Track says:

    Nocturnal: “When i deleted ‘id’ as a field name it worked instantly.”

    Thanks this solved my problem.

  9. dharma kshetri says:

    Hello Robert, I follow you great tutorials, but unable to store value on localstorage. would you please, help me.http://stackoverflow.com/questions/18306917/not-storing-value-on-localstorage-in-sencha

    • Robert says:

      @dharma If you put your code onto jsFiddle it would be much easier to debug… There are likely 2 problems with your code,

      1. The URL you are using is hard coded. Copy the out to a local file, get it working locally first!

      2. You are not creating a proper record to save locally, at the moment you have,

      var rec = {
      name : record.data.category_name+ ' (from localStorage)'
      };

      But your tpl uses “{category_name}” and your model expects “cat_id”, try something like (in your default.controller.Core file),

      var rec = {
      cat_id: record.data.cat_id,
      category_name: record.data.category_name+ ' (from localStorage)'
      };

  10. Robert! Great sample code. Saved me hours of frustration. Appreciate you posting it like this.

  11. Ashley says:

    Hi Robert, really good tutorial but I can’t get it working using your example or on my own project. Your project just goes straight in to offline mode even though I’m online and my project won’t load any data at all. I’m testing it using MAMP and virtualhostx and my own project is using the MVC style. Could I send you either one for you to take a look at please?

    Thanks.

  12. RJ says:

    Excellent tutorial. This was a big help. May as well thank you for your other tuts as well… you’re a great resource.

  13. mary says:

    Just want to make sure I understand completely. So if I run this at least once on my phone or android tablet for example online, even if I clear the browser cache, the localstorage will still work or does it no longer work once I clear browser cache? I am really trying to avoid native app packaging if this will work offline as I need ot to. Thanks in advance for your post and help!

    • Robert says:

      Correct, it will work if only the cookies are cleared. If you are running the app in a regular mobile browser (not as an app), then on some devices (iOS) there is an option in the browser (access view the Safair menu in the Settings menu/app) to clear “cookies and data”, performing this action will empty the localStorage as well as clear the cookies.

      • mary says:

        I see, that’s what I was afraid of. Pardon me for one more question if you don’t mind. There is no way to run this outside the browser as an app without packaging for a native app correct? In other words unless I package it as a native app (phonegap for ex), it must run through a browser and as soon as user clears the browser cache, the offline working app is out the window. I was just hoping for a way to port the sencha app onto multiple devices without the hassle of the phonegap conversion. Its a simple process when it works, but I’m finding lots of problems with things not running in particular the newer 2.3 grid. The grid renders but no data even though the store shows as loaded. Only a problem when I convert to native app via phonegap :(. Sorry, off the topic. Again I thank you very much for your time!

1 Pings/Trackbacks for "Sencha Touch 2 LocalStorage example"
  1. […] In depth technical posts covering complex issues (ala Sencha Touch Local Storage) […]

Leave a Reply

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

*