12/08/2018, 17:22

LocalStorage với BackBoneJS

1. Giới thiệu về backbone.localstorage Backbone thường gọi các methods fetching và lưu trữ data vào các models. Tuy nhiên, chúng tôi muốn một ứng dụng hoạt động ngoại tuyến và đồng bộ hóa với máy chủ khi trực tuyến. Vì vậy, chúng tôi yêu cầu mô hình giao tiếp với cả servers và lbrowser's ...

1. Giới thiệu về backbone.localstorage

Backbone thường gọi các methods fetching và lưu trữ data vào các models. Tuy nhiên, chúng tôi muốn một ứng dụng hoạt động ngoại tuyến và đồng bộ hóa với máy chủ khi trực tuyến. Vì vậy, chúng tôi yêu cầu mô hình giao tiếp với cả servers và lbrowser's local storage.. Backbone đã cung cấp 1 extension "backbone.localstorage.js" giúp ta có thể giao tiếp đc với local storage. Tuy nhiên mở rộng này ko thể giúp ta có thể giao tiếp giữa backbone and the server.Do đó, chúng tôi đã sử dụng backbone.localstorage.js để các mô hình của chúng tôi được lưu trữ trong local storage nhưng đã viết mã của chúng tôi để đồng bộ hóa dữ liệu cục bộ của chúng tôi với server.

2. Backbon.localstorage

Bước đầu tiên là giữ cho hàm backbone.js backbone.sync (...) trước khi backbone.localstorage.js thay thế nó. Điều đó chỉ đơn giản là để thực thi đoạn mã sau giữa việc thực hiện backbone.js và backbone.localstorage.js:

Backbone.serverSync = Backbone.sync;

và có thể save data lên server thông qua câu lệnh :

Backbone.serverSync('update', model, options);

trong khi các mô hình đang sử dụng model.fetch () và model.save () được sử dụng ở nơi khác trong mã sẽ sử dụng bộ nhớ cục bộ thông qua backbone.localstorage.js's Backbone.sync (...).

Sau đó, chúng tôi cần làm là viết mã đồng bộ bằng cách sử dụng Backbone.serverSync (...), model.fetch () và model.save ().

// Generated by CoffeeScript 1.6.3
(function() {
  (function(global, _, Backbone) {
    global.Offline = {
      VERSION: '0.5.0',
      localSync: function(method, model, options, store) {
        var resp, _ref;
        resp = (function() {
          switch (method) {
            case 'read':
              if (_.isUndefined(model.id)) {
                return store.findAll(options);
              } else {
                return store.find(model, options);
              }
              break;
            case 'create':
              return store.create(model, options);
            case 'update':
              return store.update(model, options);
            case 'delete':
              return store.destroy(model, options);
          }
        })();
        if (resp) {
          return options.success((_ref = resp.attributes) != null ? _ref : resp, options);
        } else {
          return typeof options.error === "function" ? options.error('Record not found') : void 0;
        }
      },
      sync: function(method, model, options) {
        var store, _ref;
        store = model.storage || ((_ref = model.collection) != null ? _ref.storage : void 0);
        if (store && (store != null ? store.support : void 0)) {
          return Offline.localSync(method, model, options, store);
        } else {
          return Backbone.ajaxSync(method, model, options);
        }
      },
      onLine: function() {
        return navigator.onLine !== false;
      }
    };
    Backbone.ajaxSync = Backbone.sync;
    Backbone.sync = Offline.sync;
    Offline.Storage = (function() {
      function Storage(name, collection, options) {
        this.name = name;
        this.collection = collection;
        if (options == null) {
          options = {};
        }
        this.support = this.isLocalStorageSupport();
        this.allIds = new Offline.Index(this.name, this);
        this.destroyIds = new Offline.Index("" + this.name + "-destroy", this);
        this.sync = new Offline.Sync(this.collection, this);
        this.keys = options.keys || {};
        this.autoPush = options.autoPush || false;
      }

      Storage.prototype.isLocalStorageSupport = function() {
        var e;
        try {
          localStorage.setItem('isLocalStorageSupport', '1');
          localStorage.removeItem('isLocalStorageSupport');
          return true;
        } catch (_error) {
          e = _error;
          return false;
        }
      };

      Storage.prototype.setItem = function(key, value) {
        var e;
        try {
          return localStorage.setItem(key, value);
        } catch (_error) {
          e = _error;
          if (e.name === 'QUOTA_EXCEEDED_ERR') {
            return this.collection.trigger('quota_exceed');
          } else {
            return this.support = false;
          }
        }
      };

      Storage.prototype.removeItem = function(key) {
        return localStorage.removeItem(key);
      };

      Storage.prototype.getItem = function(key) {
        return localStorage.getItem(key);
      };

      Storage.prototype.create = function(model, options) {
        if (options == null) {
          options = {};
        }
        options.regenerateId = true;
        return this.save(model, options);
      };

      Storage.prototype.update = function(model, options) {
        if (options == null) {
          options = {};
        }
        return this.save(model, options);
      };

      Storage.prototype.destroy = function(model, options) {
        var sid;
        if (options == null) {
          options = {};
        }
        if (!(options.local || (sid = model.get('sid')) === 'new')) {
          this.destroyIds.add(sid);
        }
        return this.remove(model, options);
      };

      Storage.prototype.find = function(model, options) {
        if (options == null) {
          options = {};
        }
        return JSON.parse(this.getItem("" + this.name + "-" + model.id));
      };

      Storage.prototype.findAll = function(options) {
        var id, _i, _len, _ref, _results;
        if (options == null) {
          options = {};
        }
        if (!options.local) {
          if (this.isEmpty()) {
            this.sync.full(options);
          } else {
            this.sync.incremental(options);
          }
        }
        _ref = this.allIds.values;
        _results = [];
        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
          id = _ref[_i];
          _results.push(JSON.parse(this.getItem("" + this.name + "-" + id)));
        }
        return _results;
      };

      Storage.prototype.s4 = function() {
        return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
      };

      Storage.prototype.incrementId = 0x1000000;

      Storage.prototype.localId1 = ((1 + Math.random()) * 0x100000 | 0).toString(16).substring(1);

      Storage.prototype.localId2 = ((1 + Math.random()) * 0x100000 | 0).toString(16).substring(1);

      Storage.prototype.mid = function() {
        return ((new Date).getTime() / 1000 | 0).toString(16) + this.localId1 + this.localId2 + (++this.incrementId).toString(16).substring(1);
      };

      Storage.prototype.guid = function() {
        return this.s4() + this.s4() + '-' + this.s4() + '-' + this.s4() + '-' + this.s4() + '-' + this.s4() + this.s4() + this.s4();
      };

      Storage.prototype.save = function(item, options) {
        var newId, _ref, _ref1;
        if (options == null) {
          options = {};
        }
        if (options.regenerateId) {
          newId = options.id === 'mid' ? this.mid() : this.guid();
          item.set({
            sid: ((_ref = item.attributes) != null ? _ref.sid : void 0) || ((_ref1 = item.attributes) != null ? _ref1.id : void 0) || 'new',
            id: newId
          });
        }
        if (!options.local) {
          item.set({
            updated_at: (new Date()).toJSON(),
            dirty: true
          });
        }
        this.replaceKeyFields(item, 'local');
        this.setItem("" + this.name + "-" + item.id, JSON.stringify(item));
        this.allIds.add(item.id);
        if (this.autoPush && !options.local) {
          this.sync.pushItem(item);
        }
        return item;
      };

      Storage.prototype.remove = function(item, options) {
        var sid;
        if (options == null) {
          options = {};
        }
        this.removeItem("" + this.name + "-" + item.id);
        this.allIds.remove(item.id);
        sid = item.get('sid');
        if (this.autoPush && sid !== 'new' && !options.local) {
          this.sync.flushItem(sid);
        }
        return item;
      };

      Storage.prototype.isEmpty = function() {
        return this.getItem(this.name) === null;
      };

      Storage.prototype.clear = function() {
        var collectionKeys, key, keys, record, _i, _j, _len, _len1, _ref, _results,
          _this = this;
        keys = Object.keys(localStorage);
        collectionKeys = _.filter(keys, function(key) {
          return (new RegExp(_this.name)).test(key);
        });
        for (_i = 0, _len = collectionKeys.length; _i < _len; _i++) {
          key = collectionKeys[_i];
          this.removeItem(key);
        }
        _ref = [this.allIds, this.destroyIds];
        _results = [];
        for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) {
          record = _ref[_j];
          _results.push(record.reset());
        }
        return _results;
      };

      Storage.prototype.replaceKeyFields = function(item, method) {
        var collection, field, newValue, replacedField, wrapper, _ref, _ref1, _ref2;
        if (Offline.onLine()) {
          if (item.attributes) {
            item = item.attributes;
          }
          _ref = this.keys;
          for (field in _ref) {
            collection = _ref[field];
            replacedField = item[field];
            if (!/^w{8}-w{4}-w{4}/.test(replacedField) || method !== 'local') {
              newValue = method === 'local' ? (wrapper = new Offline.Collection(collection), (_ref1 = wrapper.get(replacedField)) != null ? _ref1.id : void 0) : (_ref2 = collection.get(replacedField)) != null 
                                          
0