Change adapter at runtime


#1

Hi,

I want my app to work online and offline, if I am online I want to persist data on rest server, if offline to local storage. So for the same model can I change adapter based on online/offline mode during runtime?


#2

Quick idea: it would be possible to use a switching adapter, using the strategy pattern to persist to either online/offline depending on connection state.


#3

REST or Json adapter has a ajax() method used for all API calls and it is provided with the context. You can extend an adapter and override that method.


#4

I’m not really sure of a way that doesn’t use private APIs.

Setting the adapter is easy, store.set('adapter', 'offline');. However, ember-data internally caching the adapter instances so it doesn’t have to try and resolve them in the future. That happens here https://github.com/emberjs/data/blob/v2.2.1/packages/ember-data/lib/system/store.js#L2010 which stores it in the _instanceCache. You would need to flush that, and I’m unsure if there is a public API for that.

Perhaps you might have an easier time extending the rest/json adapter to be aware of offline vs online versus changing the adapter instance itself.


#5

Yep, this is why it must be a single instance, and why I was suggesting the strategy pattern. I don’t really have time to build a full implementation, but the idea would be along those lines:

  1. extend a basic adapter.

  2. in its init, have it create the two adapters, for offline and online storage. Something like this:

     this._adapters = {
         online: this.container.lookup('adapter:-json-api'),
         offline: this.container.lookup('adapter:-local-storage')
     };
    

    (-local-storage is not part of ember-data, you still have to do it yourself, or find one in npm).

  3. Add a method to select current strategy:

     setMode: function (mode) {
         this._current = this._adapters[mode];
     }
    

    I guess you could just have a mode property and an observer, but for something like this, I like the ceremony of calling a function.

  4. Make all methods of your adapter forward to this._current. Either statically:

     createRecord: function(...args) { return this._current.createRecord(...args); }
    

    Or if you want to go fancy you could make a forward generator:

     function forward(name) {
         return function (...args) {
             return this._current[name](...args);
         }
     }
    
     // In the adapter:
     createRecord: forward('createRecord'),
     deleteRecord: forward('deleteRecord'),
     …
    

If you are serious about providing seamless continuity between offline and online mode, you should probably trigger some syncing between local and remote data when setMode('online') is called.