Polymer でローカルストレージを使う
Polymer でローカルストレージを利用したい場合、app-storage の app-localstorage-document
タグを利用します。
iron-localstorageは現時点で
deprecated
になっています。
app-localstorage-document
タグはローカルストレージに変更があったとき data-changed
というイベントを発火します。
このイベントがきたら、 event.detail.value
もしくは、タグの data
属性から値を取得することができます。
例えば以下のように記述します。
<dom-module id="demo">
<template>
<app-localstorage-document id="storage" key="hoge"></app-localstorage-document>
</template>
<script>
Polymer({
is: 'demo',
listeners: {
'data-changed': '_dataChanged',
},
setValue: function(value) {
this.$.storage.set('data', value);
},
_dataChanged: function(event) {
console.log(event.detail.value);
},
});
</script>
</dom-module>
demo
エレメントを使ってブラウザを複数のタブで開くと、setValue
したタブの値が、他のタブではコンソールに出力されます。
現状
この実装を様々なブラウザ(最新版)で試してみたところ、下記のような状況でした。
Chrome | Firefox | Safari | IE11 | Edge |
---|---|---|---|---|
OK | OK | OK | NG | NG |
app-localstorage-document タグの実装を確認してみると、以下のようになっています。
attached: function() {
this.listen(window, 'storage', '__onStorage');
this.listen(
window.top,
'app-local-storage-changed',
'__onAppLocalStorageChanged');
},
window.onstorage
イベントでローカルストレージの変更を受け取って、__onStorage
を呼び出しています。
__onStorage: function(event) {
if (event.key !== this.key ||
event.storageArea !== this.storage) {
return;
}
this.syncToMemory(function() {
this.set('data', this.__parseValueFromStorage());
});
},
__onStorage
では、__parseValueFromStorage
を呼び出しています。
__parseValueFromStorage: function() {
try {
return JSON.parse(this.storage.getItem(this.key));
} catch(e) {
console.error('Failed to parse value from storage for', this.key);
}
},
__parseValueFromStorage
では、 this.storage
で実際のローカルストレージから値を取得しています。
MS系のブラウザでは、window.onstorage
イベントのタイミングでは、ストレージの内容は書き換わっていないようです。
(実際に開発者ツールを使って確認すると、イベント受信時はローカルストレージの値は古いままだったのですが、イベント処理が終わったあとで確認すると、新しい値になっていました。)
対応
https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onstorage
のサイトを確認すると、イベントオブジェクトには newValue
と oldValue
が設定されていることがわかりました。
実際にその値をチェックすると、IE11やEdgeでも変更された値が入っていることがわかりました。
なので、先ほどの例を簡単に書き換えると、以下のようになります。
<dom-module id="demo">
<template>
<app-localstorage-document id="storage" key="hoge"></app-localstorage-document>
</template>
<script>
Polymer({
is: 'demo',
setValue: function(value) {
this.$.storage.set('data', value);
},
_dataChanged: function(event) {
console.log(event.newValue);
},
ready: function() {
this.listen(window, 'storage', '_dataChanged');
}
});
</script>
</dom-module>
これでIE11やEdgeでも値の同期を行うことができるようになります。
ただし、このコードには問題があって、ローカルストレージのキーのチェックなどはしていないので、前述の __onStorage
のようにチェックは必要になると思います。
issue を書いた
上記内容を issue にして上げておいたので、修正されると良いなーと思っています。
(時間があったらPRしたいとも思っている)