この投稿が少し古いことは承知していますが、Immutable JS と Polymer を一緒に使用することを検討したかったので、答えたいと思いました。理由は次のとおりです。
- 私のチームはポリマーを使用してアプリケーションを構築していますが、それを変更することはできません
- 不変は元に戻すとやり直しの実装を簡単にし、それは要件です
不変のjsオブジェクトをポリマーにバインドする方法を見つけましたが、見苦しくハッキーであることを警告してください。
不変オブジェクトへのバインディング
ポリマーが不変の js プロパティにアクセスできるようにするには、エレメント プロトタイプにタイプのプロパティを作成する必要があります。これObject
は、不変の js とポリマーの間の一種の「アダプター」として機能します。
ポリマー API が の形式でオブジェクトにアクセスできるように、このオブジェクト プロパティは JavaScriptゲッター(または) を使用する必要がありますが、実際の実装では不変オブジェクトにアクセスします。Object.defineProperty
object.property
let immutableMap = Immutable.map({count: 0, text: 'world'});
Polymer({
// ...
properties: {
/**
* This property is an 'adapter' to assign a property path to access the immutablejs object.
* The property starts with an underscore because we don't want the user to set it
*/
_adapter: {
type: Object,
value: {
/**
* the properties of this object use
* [getters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get)
* so that polyer can access the object like `obj.count`
*/
get count() { return immutableMap.get('count'); },
get text() { return immutableMap.get('text'); }
}
}
}
// ...
});
上記の例では、 と の 2 つのメソッドを持つ type のプロパティを作成しましObject
た。これらのメソッドは先頭に が付加されているため、メソッド名だけでアクセスできると想定されています。これはgetterです。count
text
get
これを行うと、ポリマーがオブジェクトにバインドできるようになり_adapter
、そのオブジェクトは から値を「取得」しますimmutableMap
。
不変オブジェクトの変更
これを機能させるために必要な 2 番目の要素は、不変オブジェクトを変更することです。オブジェクトは不変であるため、双方向バインディングはオプションではありません。不変を使用する場合、不変オブジェクトを再割り当てして再レンダリングするイベント リスナーを追加する必要があります。
そのためには、 Polymer のダーティ チェックをオーバーライドする必要があります。使用notifyPath
してみましたが、例外として機能しないことがわかりました。 notifyPath
動作するはずです。
警告
ダーティ チェックをオーバーライドしていて、仮想 DOM ライブラリ (react など) を使用していないため、不変オブジェクトを使用する_adapter
には、不変オブジェクトで変更が発生するたびに を使用する要素を再レンダリングする必要があります。これは、使用することで改善できますnotifyPath
。
完全なデモ (取り消しあり)
これは、不変の js と Polymer を組み合わせた完全なデモです。不変を使用する少しのインセンティブのために元に戻す機能を追加しました-不変のJSは元に戻すとやり直すを非常に簡単にします:D
<base href="https://cdn.rawgit.com/download/polymer-cdn/1.7.0/lib/">
<script src="webcomponentsjs/webcomponents-lite.min.js"></script>
<link rel="import" href="polymer/polymer.html">
<script src="https://cdnjs.cloudflare.com/ajax/libs/immutable/3.8.1/immutable.min.js"></script>
<!-- Defines element markup -->
<dom-module id="hello-world">
<template>
<div>
<!--use only one-way binding because the object is immutable-->
<button on-tap="onButtonClick">clicks <span>[[_adapter.count]]</span></button>
</div>
<div>
<input on-input="onInput" type="text" value="[[_adapter.text]]">
<h1>hello, <span>[[_adapter.text]]</span>!</h1>
</div>
<div>
<button on-tap="onUndo">Undo</button>
</div>
</template>
<!-- Registers custom element -->
<script>
// immutable map is defined out of the scope of the polymer object
let immutableMap = Immutable.Map({
count: 0,
text: 'world'
});
// add undos
let undos = [];
Polymer({
is: 'hello-world',
/**
* every event should reassign the `immutableMap` then manually `notifyPath`
*/
onButtonClick: function () {
undos.push(immutableMap);
immutableMap = immutableMap.update('count', count => count + 1);
this.notifyPath('_adapter.count');
},
onInput: function (event) {
undos.push(immutableMap);
immutableMap = immutableMap.set('text', event.target.value);
this.notifyPath('_adapter.text');
},
onUndo: function () {
if (undos.length) {
immutableMap = undos.pop();
}
const obj = this._adapter;
this._adapter = {};
this._adapter = obj;
},
properties: {
/**
* This property is an 'adapter' to assign a property path to access the immutablejs object.
* The property starts with an underscore because we don't want the user to set it
*/
_adapter: {
type: Object,
value: {
/**
* the properties of this object use
* [getters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get)
* so that polymer can access the object like `obj.count`
*/
get count() { return immutableMap.get('count'); },
get text() { return immutableMap.get('text'); }
}
}
}
});
</script>
</dom-module>
<hello-world></hello-world>