2013-02-11 11 views
41

Emberを使用してリモートオブジェクトをロードおよびキャッシュする方法について質問があります。私はREST APIを介してサーバー側のストレージを使用するEmberアプリケーションを開発しています。フェッチされたデータの一部はほとんど変更されないため、アプリケーションをロードするたびにサーバーからフェッチする必要はありません。しかし、これはまた、オフラインで作業し、そのデータをサーバーに保存する必要があるアプリケーションの問題です。EmberDataを使用してローカルストレージにリモートデータをキャッシュする

Ember Dataには、REST APIを介して永続モデル用の内蔵ストレージアダプタがあり、an adapter for Local Storageもあります(下記のKenが指摘しています)。この問題は、モデルに1つのストレージアダプタしかなく、フェッチされたモデルをメモリ内に保持する以外の方法はないと思われます。

このEmber wishlistのコメントとこのtalk by Tom Daleのコメントで同様のリクエストが見つかりましたが、これはEmberの既存の機能であることを示していません。

I持っている二つの質問(重要なものである第1の1):

  1. 最良の方法は何ですか - 今日 - ローカルストレージにキャッシュされたモデルを実装し、必要に応じて、リモート・データとそれらを同期するには?
  2. これはEmberに含まれる予定の機能ですか、それとも管理者が最終的に追加する必要があるものなのでしょうか?

それが1になる)、私は戦略のカップルを考えることができます。

a)は、既存のアダプターを拡張し、カスタムリモート同期メカニズムを追加:

App.Store.registerAdapter('App.Post', DS.LSAdapter.extend({ 
    // do stuff when stuff happens 
})); 

B)を維持別々のモデルクラス(リモートオブジェクト用に1セット、ローカルオブジェクト用に1セット)を作成し、必要に応じてそれらの間で同期をとります。標準藤堂ケースで:

RemoteTodo –*sync*– Todo 
        | 
        UI 

私はちょっと、これは本当のnoobの質問であり、このための良い確立したパターンがあることを願っています。

更新日: Found this similar questionそれは良い答えがありますが、それは理論的なものです。私は、実用的なヒントや実装例への指針が必要だと思います。

答えて

3

有用なローカルストレージアダプタの実装があります。それは私が安らかなAPIの燃えさ​​しローカルキャッシュなど:

ダンGebhardtはそうのためのソリューションを調査し、トップの結果の一つだったので、少しこのスレッドを「バンプ」するだけでhttps://github.com/rpflorence/ember-localstorage-adapter

+0

感謝を使用している - 私は実際に目そのアダプタはEmber Dataの一部であったはずです。私はそれに応じて私の質問を更新します。LSキャッシングでRESTバックエンドを使用する良い方法を探しているので、実際には私の問題を解決することはできません。自分のデータをLSに独占的に保存しないでください。 –

+0

同じアプリケーション内で異なるアダプタを使用することができます。選択したアダプタを特定のモデルに割り当てることができます。 – ken

+4

それは私の質問でb)を実装する方法だろう。私はまだ最善のアプローチについて疑問に思っています。 –

5

を見てくださいエンバーにOrbit.jsとの統合で流血の良い仕事をします https://github.com/orbitjs/ember-orbit

軌道は、データソース同期 と保つその内容へのアクセスを調整するためのスタンドアロンライブラリです。

オービットはスタックやアドホック編集 コンテキストをアンドゥ/リドゥなどのオフライン操作、保守およびローカル・キャッシュの 同期として クライアント側アプリケーションに高度な機能を構築するための基盤を提供します。

Orbit.js機能:

  • アプリケーションに異なるデータソースの任意の数をサポートし、共通インターフェースを介して、それらへのアクセスを提供します。

  • 優先と代替計画を指定する機能など、異なるソースによって要求の履行のために許可します。

  • は、レコードが同時にソース間で異なる状態で存在することを許可します。

  • ソース間の座標変換。可能であれば、自動的にハンドルをマージしますが、完全なカスタムコントロールが可能です。

  • ブロックおよび非ブロック変換を許可します。

  • 同期要求と非同期要求を許可します。

  • サポートトランザクションおよび動作の逆を追跡することによって/リドゥを取り消します。

  • プレーンJavaScriptオブジェクトで作業します。

軌道についての彼の偉大なスピーチやスライドを逃してはいけない:
Introduction to Orbit.js

UPDATE:私の投稿がdownvotedてしまったように私は、軌道のページからいくつかのより多くの記述情報を追加しました「ただ」外部リソースを参照し、自身で実際のソリューションが含まれていない。しかし、軌道のための解決策、そして、これはここにリンクを介してである「含める」する唯一の方法のように私には思える。)

+0

これらのリンクが質問に答えるかもしれませんが、回答の重要な部分をここに含めて参考にしてください。リンクされたページが変更された場合、リンクのみの回答は無効になります。 – Onik

1

これを行う方法があります。あなたのアダプター用のミックスインでは、メソッドlocalStoreRecordを使用してレコードをキャッシュし、最後にストアをプリロードするイニシャライザーを使用できます。文字列化されたオブジェクトのvalueストア、私たちは一つのキーの下で、当社のアプリケーションデータのすべてを保存することができます。

ローカルストレージは、単にキーです。

注:これはES6モジュール

// app/mixins/local-storage.js 
 

 
import Ember from 'ember'; 
 

 
export default Ember.Mixin.create({ 
 
    appName: 'myApp', 
 
    // how many records per model to store locally, can be improved. 
 
    // needed to prevent going over localStorage's 5mb limit 
 
    localStorageLimit: 5, 
 
    localStoreRecord: function(record) { 
 
    var data = JSON.parse(localStorage.getItem(this.appName)); 
 
    data = data || {}; 
 
    data[this.modelName] = data[this.modelName] || []; 
 
    var isNew = data[this.modelName].every(function(rec) { 
 
     rec.id !== record.id; 
 
    }); 
 
    if (isNew) { 
 
     data[this.modelName].push(record); 
 
     if (data[this.modelName].length > this.localStorageLimit) { 
 
     data[this.modelName].shift(); 
 
     } 
 
     localStorage.setItem(this.appName, JSON.stringify(data)); 
 
    } 
 
    } 
 
});

// app/adapters/skateboard.js 

import DS from 'ember-data'; 
import Ember from 'ember'; 
import LocalStorageMixin from '../mixins/local-storage'; 

export default DS.RESTAdapter.extend(LocalStorageMixin, { 
    modelName: 'skateboard', 
    find: function(store, type, id) { 
    var self = this; 
    var url = [type,id].join('/'); 
    return new Ember.RSVP.Promise(function(resolve, reject) { 
     Ember.$.ajax({ 
     url: 'api/' + url, 
     type: 'GET' 
     }).done(function (response) { 
     // cache the response in localStorage 
     self.localStoreRecord(response); 
     resolve({ type: response }); 
     }).fail(function(jqHXR, responseStatus) { 
     reject(new Error(type + 
     ' request failed with status=' + reponseStatus); 
     }); 
    }); 
    }, 
    updateRecord: function(store, type, record) { 
    var data = this.serialize(record, { includeId: true }); 
    var id = record.get('id'); 
    var url = [type, id].join('/'); 
    return new Ember.RSVP.Promise(function(resolve, reject) { 
     Ember.$.ajax({ 
     type: 'PUT', 
     url: 'api/' + url, 
     dataType: 'json', 
     data: data 
     }).then(function(data) { 
     // cache the response in localStorage 
     self.localStoreRecord(response); 
     resolve({ type: response }); 
     }).fail(function(jqXHR, responseData) { 
     reject(new Error(type + 
     ' request failed with status=' + reponseStatus); 
     }); 
    }); 
    } 
}); 

// app/initializers/local-storage.js 
 

 
export var initialize = function(container/*, application*/) { 
 
    var appName = 'myApp'; 
 
    var store = container.lookup('store:main'); 
 
    var data = JSON.parse(localStorage.getItem(appName)); 
 
    console.log('localStorage:',data); 
 
    if (!data) { 
 
    return; 
 
    } 
 
    var keys = Object.keys(data); 
 
    if (keys.length) { 
 
    keys.forEach(function(key) { 
 
     console.log(key,data[key][0]); 
 
     store.createRecord(key, data[key][0]); 
 
    }); 
 
    } 
 
}; 
 

 
export default { 
 
    name: 'local-storage', 
 
    after: 'store', 
 
    initialize: initialize 
 
};
ポインタのための

+1

ここにいくつかの説明を追加するとよいでしょう。 –

関連する問題