Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

ember-localforage

bullet-db24MIT1.5.9

The addon provides an ember-data adapter and a storageFor computed property that persists the changes to localforage

ember-addon, localStorage, sessionStorage, local-storage, local-storage array, local-storage object, session-storage, session-storage array, session-storage object, localStorage adapter, local-storage adapter

readme

Ember localStorage

Build Status Ember Observer Score Greenkeeper badge

The addon provides a storageFor computed property that returns a proxy and persists the changes to localStorage or sessionStorage. It works with objects and arrays and has a generator to create the proxy objects or arrays.

It ships with an ember-data adapter that works almost the same as the JSONAPIAdapter with some relationship sugar added.

The idea was taken from Tom Dale's gist Ember Array that writes every change to localStorage and extended to objects. The storageFor API was inspired by Ember State Services.

Installation

  • ember install ember-local-storage

Changelog

See the CHANGELOG

Usage

The documentation is for versions >= 1.0.0 if you are looking for older versions look here.

If you upgrade from a version <= 0.1.5 you need to set a legacyKey on the computed storageFor:

export default Ember.Component.extend({
  settings: storageFor('settings', { legacyKey: 'your-old-key' })
});

Object & Array

Object

Run ember g storage -h for all options.

ember g storage stats
// will generate a localStorage object

ember g storage stats -s
// will generate a sessionStorage object
// app/storages/stats.js
import StorageObject from 'ember-local-storage/local/object';

const Storage = StorageObject.extend();

Storage.reopenClass({
  initialState() {
    return { counter: 0 };
  }
});

export default Storage;
// app/controllers/application.js
import Ember from 'ember';
import { storageFor } from 'ember-local-storage';

export default Ember.Controller.extend({
  stats: storageFor('stats'),

  actions: {
    countUp() {
      this.incrementProperty('stats.counter');
    },
    resetCounter() {
      this.get('stats').clear();
      // or
      // this.get('stats').reset();
      // this.set('stats.counter', 0);
    }
  }
});
{{! app/templates/application.hbs}}
<button {{action "countUp"}}>Page Visits: {{stats.counter}}</button>
<button {{action "resetCounter"}}>X</button>

Array

Run ember g storage -h for all options.

ember g storage anonymous-likes -a
// will generate a localStorage array

ember g storage anonymous-likes -a -s
// will generate a sessionStorage array
// app/storages/anonymous-likes.js
import StorageArray from 'ember-local-storage/local/array';

const Storage = StorageArray.extend();

// Uncomment if you would like to set initialState
// Storage.reopenClass({
//   initialState() {
//     return [];
//   }
// });

export default Storage;
// app/components/like-item.js
import Ember from 'ember';
import { storageFor } from 'ember-local-storage';

export default Ember.Component.extend({
  anonymousLikes: storageFor('anonymous-likes'),

  isLiked: computed('id', function() {
    return this.get('anonymousLikes').includes(this.get('id'));
  }),

  actions: {
    like: function(id) {
      this.get('anonymousLikes').addObject(id);
    }
  }
});
{{! app/templates/components/like-item.hbs}}
{{#unless isLiked}}
  <button {{action "like" id}}>Like it</button>
{{else}}
  You like it!
{{/unless}}

storageFor options

storageFor(key, model, options)

key String - The filename of the storage (e.g. stats)

model Optional string - The dependent property. Must be an ember data model or an object with modelName and id properties. (It is still experimental)

options are:

  • legacyKey String

Methods

The following methods work on StorageObject and StorageArray

.isInitialContent()

You can call .isInitialContent() to determine if content is equal to initialState. Returns a boolean.

.reset()

You can invoke .reset() to reset the content to the initialState.

.clear()

You can invoke .clear() to remove the content from xStorage.

Adapter & Serializer

Important: The Adapter works with ember-data versions >= 1.13 because it depends on JSONAPIAdapter.

If your app is a pure LocalStorage app you just need to create the application adapter and serializer:

// app/adapters/application.js
export { default } from 'ember-local-storage/adapters/local';
// or export { default } from 'ember-local-storage/adapters/session';

// app/serializers/application.js
export { default } from 'ember-local-storage/serializers/serializer';

If you already use Ember Data for non LocalStorage models you can use a per type adapter and serializer.

// app/adapters/post.js
export { default } from 'ember-local-storage/adapters/local';
// or export { default } from 'ember-local-storage/adapters/session';

// app/serializers/post.js
export { default } from 'ember-local-storage/serializers/serializer';

If you use namespaced models e.g. blog/post you have to add the modelNamespace property to the corresponding adapter:

// app/adapters/blog/post.js
import Adapter from 'ember-local-storage/adapters/local';
// or import Adapter from 'ember-local-storage/adapters/session';

export default Adapter.extend({
  modelNamespace: 'blog'
});

Model

Your model is a DS.Model with two new relationship options

// app/models/post.js
import DS from 'ember-data';

const {
  Model,
  attr,
  hasMany
} = DS;

export default Model.extend({
  name: attr('string'),

  comments: hasMany('comment', { async: true, dependent: 'destroy' })
});

// app/models/comment.js
import DS from 'ember-data';

const {
  Model,
  attr,
  belongsTo
} = DS;

export default Model.extend({
  name: attr('string'),

  post: belongsTo('post', { async: true, autoSave: true })
});

Options

  • dependent can be used in hasMany relationships to destroy the child records when the parent record is destroyed.
  • autoSave can be used in belongsTo relationships to update the association on the parent. It's recommended to use it.

.query() & .queryRecord()

As per ember guides you can query for attributes:

  // with a string
  this.store.query('post', { filter: { name: 'Just a name' } });

  // or a regex
  this.store.query('post', { filter: { name: /^Just(.*)/ } });

Querying relationships also works:

// belongsTo
// get posts from user '123'
this.store.query('post', { filter: { user: '123' } });
this.store.query('post', { filter: { user: { id: '123' } } });
// or regex
this.store.query('post', { filter: { user: /^12/ } });
this.store.query('post', { filter: { user: { id: /^12/ } } });

// belongsTo polymorphic
// get posts from editors
this.store.query('post', { filter: { user: { type: 'editor' } } });
this.store.query('post', { filter: { user: { type: /^ed(.*)ors$/ } } }); // you need to use the plural
// get posts from editor '123'
this.store.query('post', { filter: { user: { id: '123', type: 'editor' } } });
this.store.query('post', { filter: { user: { id: '123', type: /^ed(.*)ors$/ } } }); // you need to use the plural

// hasMany
// get users who've contributed to project.id = 123
this.store.query('user', { filter: { projects: '123' } });
this.store.query('user', { filter: { projects: { id: '123' } } });
// or regex
this.store.query('user', { filter: { projects: /^12/ });
this.store.query('user', { filter: { projects: { id: /^12/ } } });

// hasMany polymorphic
// get users with cats
this.store.query('user', { filter: { pets: { type: 'cat' } } });
// get users with cat '123'
this.store.query('user', { filter: { pets: { id: '123', type: 'cat' } }) };
// get users with cats AND dogs
this.store.query('user', { filter: { pets: [{ type: 'cat' }, { type: 'dog' }] } });
// get users with cats OR dogs
this.store.query('user', { filter: { pets: { type: /cats|dogs/ } } }); // you need to use the plural

You can use queryRecord to return only one record. See the guides for an example.

Export & Import

The addon ships with an initializer that enables export and import of you LocalStorage data. You have to add fileExport option to the environment.js:

// config/environment.js
module.exports = function() {
  var ENV = {
    'ember-local-storage': {
      fileExport: true
    }
  }
};

The initializer provides exportData() and importData() on the store. Both return a Promise.

import Ember from 'ember';

const {
  Route
} = Ember;

export default Route.extend({
  readFile: function(file) {
    const reader = new FileReader();

    return new Ember.RSVP.Promise((resolve) => {
      reader.onload = function(event) {
        resolve({
          file: file.name,
          type: file.type,
          data: event.target.result,
          size: file.size
        });
      };

      reader.readAsText(file);
    });
  },
  actions: {
    importData: function(event) {
      this.readFile(event.target.files[0])
        .then((file) => {
          this.store.importData(file.data);
        });
    },
    exportData: function() {
      this.store.exportData(
        ['posts', 'comments'],
        {download: true, filename: 'my-data.json'}
      );
    }
  }
});

importData(content, options)

content can be a JSON API compliant object or a JSON string

options are:

  • json Boolean (default true)
  • truncate Boolean (default true) if true the existing data gets replaced.

exportData(types, options)

types Array of types to export. The types must be pluralized.

options are:

  • json Boolean (default true)
  • download Boolean (default false)
  • filename String (default ember-data.json)

Running

Running Tests

  • yarn test (Runs ember try:each to test your addon against multiple Ember versions)
  • ember test
  • ember test --server

Building

  • ember build

Publishing

ember github-pages:commit --message "New gh-pages release"

ember release
npm publish

For more information on using ember-cli, visit https://ember-cli.com/.

changelog

Changelog

1.4.1

  • [FIX] Remove singularize + pluralize deprecation warnings. Thanks to @eharrow and @CvX for the PRs.

1.4.0

  • [FIX] Namespaces storage based on type key if storageFors model option is used. Thanks to @offirgolan for the PR.
  • [ENHANCEMENT] Moving blob-polyfill dependency from bower to npm. You can remove the bower dependency if its not needed by other code. Thanks to @gmurphey for the PR.

1.3.7

  • [FIX] storageFors model option now supports latest ember-data models. Thanks to @offirgolan for the PR.

1.3.6

  • [FIX] Fix broken import/export and decouple the methods from the adapter. Thanks to @bendemboski for the PR.

1.3.5

  • [FIX] Raise AdapterError on not found. Thanks to @bendemboski for the PR.
  • [FIX] tryStorage now returns undefined instead of null. Thanks to @xcambar for the PR.

1.3.4

  • [FIX] Remove getOwner deprecation. Thanks to @bendemboski for the PR.
  • [FIX] Don't leak _indices into registry. Thanks to @bendemboski for the PR.
  • [FIX] Remove _shouldSerializeHasMany deprecation.

1.3.3

  • [FIX] Return undefined instead of null on queryFilter via queryRecord. Thanks to @bncoelho for the PR.

1.3.2

  • [FIX] Prevent leaking event listenes during testing. Thanks to @rwjblue for the PR.

1.3.1

  • [ENHANCEMENT] Replace Ember.merge with Ember.assign [deprecation id: ember-metal.merge]. Thanks to @aharita for the PR.

1.3.0

  • [ENHANCEMENT] Add session adapter. Thanks to @schickm for the implementation.

1.2.0

  • [ENHANCEMENT] A queryRecord() for empty results now returns null for ember-data versions >= 2.2.X. Thanks to EmberHH Hack Night for the time to fix this.

1.1.0

  • [ENHANCEMENT] Allow namespaced models. See the README for more information. Thanks to @juni0r for requesting.

1.0.1

  • [ENHANCEMENT] Use getOwner polyfill to remove deprecations. Thanks to @kepek for implementing.

1.0.0

  • [ENHANCEMENT] New storageFor API. See the README for more information.
  • [BREAKING] Not sure if the release is breaking. If so please open an issue.

0.1.5

  • [BUGFIX] Fixes detection of ember-data >= 2.3.0 now that it is an addon (npm package). Thanks to @Arkham for reporting.

0.1.4

  • [BUGFIX] Allow querying on boolean attributes that are false. Thanks to @Ramblurr for reporting and @bekzod for fixing.

0.1.3

  • [BUGFIX] Prevents infinite loop in IE 11 if the storage event fires in the same tab. Thanks to @MattNguyen for reporting.

0.1.2

  • [BUGFIX] Prevents the value to become null if the newValue of the storage event is undefined or null.
  • [BUGFIX] Prevents infinite loop in IE 11 if the storage event fires in the same tab.

0.1.1

  • [BUGFIX] fixes store.push() to persist the pushed payload @Ramblurr
  • [BUGFIX] normalize query filter keys (bookPublication -> book-publication) thanks to @Ramblurr for reporting
  • [BUGFIX] fixes queryRecord() for empty results. The behavior is still irritating because in case of no match we return an empty array. It's an issue with ember-data. See #3790 as soon as #3866 makes it into a release queryRecord() will return null. Thanks to @Ramblurr for reporting.

0.1.0

  • [ENHANCEMENT] Add query & queryRecord thanks to @robbiespeed for pairing
  • [ENHANCEMENT] Sync storage across tabs @davewasmer

0.0.10

  • [BUGFIX] Do not crash if ember-data isn't present

0.0.9

  • [BUGFIX] Checks if ember-data >= 1.13.0

0.0.8

  • [BUGFIX] Fixes an issue with the app config not beein present

0.0.7

0.0.6

  • [BUGFIX] Make sure that the changes are persisted on StorageArray.reset()
  • [ENHANCEMENT] Calling StorageObject.isInitialContent() and StorageArray.isInitialContent() returns a boolean that indicates if the content was mutated
  • [ENHANCEMENT] Calling StorageObject.clear() and StorageArray.clear() removes the data from xStorage
  • [ENHANCEMENT] Adds setProperties on StorageObject

0.0.5

  • [ENHANCEMENT] Prototype extensions are now disabled and the StorageArray always returns an Ember.Array @miguelcobain
  • [ENHANCEMENT] Calling StorageObject.reset() and StorageArray.reset() resets to initialContent @miguelcobain

0.0.4

  • [BUGFIX] Fixes safari private mode exposes xStorage but fails on setItem

0.0.3

  • [BUGFIX] Prevents content sharing for objects and arrays @glagola
  • [BUGFIX] Prevents the creation of a key null with the content null if the object is created with arguments
  • [ENHANCEMENT] Adds in-memory fallback for localStorage and sessionStorage

0.0.2

  • [ENHANCEMENT] sessionStorage added
  • [ENHANCEMENT] Usage of localStorageKey is deprecated use storageKey instead.
  • [BREAKING] localStorage array and object location changed
    • ember-local-storage/object -> ember-local-storage/local/object
    • ember-local-storage/array -> ember-local-storage/local/array