Polymer 0.5では、グローバルに関するアドバイスは、この質問/回答で概説したとおりでした。
ただし、Polymer 1.0では動作しないようです。変更通知は、基礎となるモデルでは自動的に生成されません。代わりに、<dom-module>
で生成されます。 <app-globals>
の1つだけで生成されます。
Polymer 1.0でこのパターンを実装する推奨方法は何ですか?
ポリマー要素 <iron-meta>
もオプションです。私にとってこれが最も簡単な解決策でした。
Etherealonesのソリューションを拡張してBehaviorとして機能し、Polymersの「set」および「notifyPath」メソッドを拡張して、更新を自動的にトリガーしました。これは、コンポーネント/要素間で真のデータバインディングに到達できる範囲に近いものです。
globals-behavior.html:
<script>
var instances = [];
var dataGlobal = {};
var GlobalsBehaviour = {
properties: {
globals: {
type: Object,
notify: true,
value: dataGlobal
}
},
ready: function() {
var _setOrig = this.set;
var _notifyPathOrig = this.notifyPath;
this.set = function() {
_setOrig.apply(this, arguments);
if (arguments[0].split(".")[0] === "globals") {
this.invokeInstances(_notifyPathOrig, arguments);
}
};
this.notifyPath = function(path, value) {
_notifyPathOrig.apply(this, arguments);
if (arguments[0].split(".")[0] === "globals") {
this.invokeInstances(_notifyPathOrig, arguments);
}
};
},
invokeInstances: function(fn, args) {
var i;
for (i = 0; i < instances.length; i++) {
instance = instances[i];
if (instance !== this) {
fn.apply(instance, args);
}
}
},
attached: function() {
instances.Push(this);
},
detached: function() {
var i;
i = instances.indexOf(this);
if (i >= 0) {
instances.splice(i, 1);
}
}
};
</script>
そして、グローバル変数にアクセスする必要があるすべてのpolymer要素で:
<script>
Polymer({
is: 'globals-enabled-element',
behaviors: [GlobalsBehaviour]
});
</script>
<!DOCTYPE html>
<html>
<head>
<title>Globals Behavior Example</title>
<link rel="import" href="//rawgit.com/Polymer/polymer/master/polymer.html">
<dom-module id="globals-enabled-element">
<template>
<input type="text" value="{{globals.my_variable::input}}">
</template>
<script>
var instances = [];
var dataGlobal = {};
var GlobalsBehaviour = {
properties: {
globals: {
type: Object,
notify: true,
value: dataGlobal
}
},
ready: function() {
var _setOrig = this.set;
var _notifyPathOrig = this.notifyPath;
this.set = function() {
_setOrig.apply(this, arguments);
if (arguments[0].split(".")[0] === "globals") {
this.invokeInstances(_notifyPathOrig, arguments);
}
};
this.notifyPath = function(path, value) {
_notifyPathOrig.apply(this, arguments);
if (arguments[0].split(".")[0] === "globals") {
this.invokeInstances(_notifyPathOrig, arguments);
}
};
},
invokeInstances: function(fn, args) {
var i;
for (i = 0; i < instances.length; i++) {
instance = instances[i];
if (instance !== this) {
fn.apply(instance, args);
}
}
},
attached: function() {
instances.Push(this);
},
detached: function() {
var i;
i = instances.indexOf(this);
if (i >= 0) {
instances.splice(i, 1);
}
}
};
</script>
<script>
Polymer({
is: 'globals-enabled-element',
behaviors: [GlobalsBehaviour]
});
</script>
</dom-module>
</head>
<body>
<template is="dom-bind">
<p>This is our first polymer element:</p>
<globals-enabled-element id="element1"></globals-enabled-element>
<p>And this is another one:</p>
<globals-enabled-element id="element2"></globals-enabled-element>
</template>
</body>
</html>
Alexei Volkovの答えを改善しようとしましたが、グローバル変数を個別に定義したかったのです。ゲッター/セッターの代わりに、observer
プロパティを使用して、インスタンスとともにキーを保存しました。
使用法は次のとおりです。
<app-data key="fName" data="{{firstName}}" ></app-data>
一方、key
propertyはグローバル変数の名前を定義します。
したがって、たとえば次を使用できます。
<!-- Output element -->
<dom-module id="output-element" >
<template>
<app-data key="fName" data="{{data1}}" ></app-data>
<app-data key="lName" data="{{data2}}" ></app-data>
<h4>Output-Element</h4>
<div>First Name: <span>{{data1}}</span></div>
<div>Last Name: <span>{{data2}}</span></div>
</template>
</dom-module>
<script>Polymer({is:'output-element'});</script>
<app-data>
domモジュールの定義:
<dom-module id="app-data"></dom-module>
<script>
(function () {
var instances = [];
var vars = Object.create(Polymer.Base);
Polymer({
is: 'app-data',
properties: {
data: {
type: Object,
value: '',
notify: true,
readonly: false,
observer: '_data_changed'
},
key: String
},
created: function () {
key = this.getAttribute('key');
if (!key){
console.log(this);
throw('app-data element requires key');
}
instances.Push({key:key, instance:this});
},
detached: function () {
key = this.getAttribute('key');
var i = instances.indexOf({key:key, instance:this});
if (i >= 0) {
instances.splice(i, 1);
}
},
_data_changed: function (newvalue, oldvalue) {
key = this.getAttribute('key');
if (!key){
throw('_data_changed: app-data element requires key');
}
vars.set(key, newvalue);
// notify the instances with the correct key
for (var i = 0; i < instances.length; i++)
{
if(instances[i].key == key)
{
instances[i].instance.notifyPath('data', newvalue);
}
}
}
});
})();
</script>
完全に動作するデモはこちら: http://jsbin.com/femaceyusa/1/edit?html,output
_iron-signals
_がこの目的で使用するようなパターンを実装しました。したがって、基本原則は、更新が発生したときに他のインスタンスに手動で通知することです。
このことを考慮:
_<dom-module id="x-global">
<script>
(function() {
var instances = [];
var dataGlobal = {};
Polymer({
is: 'x-global',
properties: {
data: {
type: Object,
value: dataGlobal,
},
},
attached: function() {
instances.Push(this);
},
detached: function() {
var i = instances.indexOf(this);
if (i >= 0) {
instances.splice(i, 1);
}
},
set_: function(path, value) {
this.set(path, value);
instances.forEach(function(instance) {
if (instance !== this) { // if it is not this one
instance.notifyPath(path, value);
}
}.bind(this));
},
notifyPath_: function(path, value) {
instances.forEach(function(instance) {
instance.notifyPath(path, value);
});
},
fire_: function(name, d) {
instances.forEach(function(instance) {
instance.fire(name, d);
});
},
});
})();
</script>
</dom-module>
_
イベントを起動するときに、_fire_
_のような下線サフィックスを持つバージョンを単純に呼び出します。 Polymer私が推測するこのパターンで何らかの振る舞いを作成することさえできます。
前のアンダースコアプロパティはPolymerによって既に使用されているため、これらを__fire
_に変換しないでください。
P.S .: this.Push(array, value);
の通知を反映する方法を解決するために、私はそれを必要としないので見回しませんでした。この方法が可能かどうかはわかりません。 _Polymer.Base.Push
_を見つけてください。
Polymerのクリエイターの1人であるSjmilesは、共有データの例として、次の snippet を Polymer slack room に投稿しました。
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="description" content="shared-data element and repeats">
<base href="http://milestech.net/components/">
<script href="webcomponentsjs/webcomponents-lite.min.js"></script>
<link href="polymer/polymer.html" rel="import">
</head>
<body>
<demo-test></demo-test>
<script>
(function() {
var private_data = [{name: 'a'}, {name: 'b'}, {name: 'c'}];
Polymer({
is: 'private-shared-data',
properties: {
data: {
type: Object,
notify: true,
value: function() {
return private_data;
}
}
}
});
})();
Polymer({
is: 'xh-api-device',
properties: {
data: {
type: Array,
notify: true
},
_share: {
value: document.createElement('private-shared-data')
}
},
observers: [
'dataChanged(data.*)'
],
ready: function() {
this.data = this._share.data;
this.listen(this._share, 'data-changed', 'sharedDataChanged');
},
dataChanged: function(info) {
this._share.fire('data-changed', info, {bubbles: false});
},
sharedDataChanged: function(e) {
this.fire(e.type, e.detail);
},
add: function(name) {
this.Push('data', {name: name});
}
});
</script>
<dom-module id="demo-test">
<template>
<h2>One</h2>
<xh-api-device id="devices" data="{{data}}"></xh-api-device>
<template is="dom-repeat" items="{{data}}">
<div>name: <span>{{item.name}}</span></div>
</template>
<h2>Two</h2>
<xh-api-device data="{{data2}}"></xh-api-device>
<template is="dom-repeat" items="{{data2}}">
<div>name: <span>{{item.name}}</span></div>
</template>
<br>
<br>
<button on-click="populate">Populate</button>
</template>
<script>
Polymer({
populate: function() {
this.$.devices.add((Math.random()*100).toFixed(2));
// this works too
//this.Push('data', {name: (Math.random()*100).toFixed(2)});
}
});
</script>
</dom-module>
</body>
</html>
実際に単純なデータバインディングを使用するようにアプリを移動したので、このアプローチの有効性はわかりませんが、誰かにとっては役立つかもしれません。
ootwch's solution を使用すると、遅延ロードされたコンポーネントで競合状態に陥りました。
投稿されたように、遅延ロードされたコンポーネントは共有データの値で初期化されません。
他の誰かが同じ問題に遭遇した場合、次のような準備の整ったコールバックを追加することで修正したと思います。
ready: function() {
const key = this.getAttribute('key')
if (!key) {
throw new Error('cm-app-global element requires key')
}
const val = vars.get(key)
if (!!val) {
this.set('data', val)
}
},
これが誰かの痛みを和らげることを願っています。
アプリケーションをテンプレートでラップすると、グローバル変数の同じ効果を実現するのがはるかに簡単になります。この説明をご覧ください ビデオ (コンセプトが説明されている正確な分と秒にリンクしています)。
上記のすべての提案を次のグローバルpolymerオブジェクトに結合しました
<dom-module id="app-data">
</dom-module>
<script>
(function () {
var instances = [];
var vars = Object.create(Polymer.Base);
var commondata = {
get loader() {
return vars.get("loader");
},
set loader(v) {
return setGlob("loader", v);
}
};
function setGlob(path, v) {
if (vars.get(path) != v) {
vars.set(path, v);
for (var i = 0; i < instances.length; i++) {
instances[i].notifyPath("data." + path, v);
}
}
return v;
}
Polymer({
is: 'app-data',
properties: {
data: {
type: Object,
value: commondata,
notify: true,
readonly: true
}
},
created: function () {
instances.Push(this);
},
detached: function () {
var i = instances.indexOf(this);
if (i >= 0) {
instances.splice(i, 1);
}
}
});
})();
</script>
そして、それを使用します
<dom-module id="app-navigation">
<style>
</style>
<template>
<app-data id="data01" data="{{data1}}" ></app-data>
<app-data id="data02" data="{{data2}}"></app-data>
<span>{{data1.loader}}</span>
<span>{{data2.loader}}</span>
</template>
</dom-module>
<script>
(function () {
Polymer({
is: 'app-navigation',
properties: {
},
ready: function () {
this.data1.loader=51;
}
});
})();
</script>
Data1.loaderまたはdata2.loaderのいずれかを変更すると、他のインスタンスに影響します。 commondataオブジェクトを拡張して、ローダープロパティで表示されるようなグローバルプロパティを追加する必要があります。