[英]Polymer 1.0 Global Variables
In Polymer 0.5 the advice on globals was as outlined in this question/answer: 在Polymer 0.5中,关于全局变量的建议如本问题/答案中所述:
Polymer global variables 聚合物全局变量
However in Polymer 1.0 this doesn't seem to work. 但是在Polymer 1.0中,这似乎不起作用。 Change notifications are not automatically generated on the underlying model, they are generated on the <dom-module>
instead which means that change notifications will be generated on only one of the <app-globals>
. 更改通知不会在底层模型上自动生成,而是在<dom-module>
上生成,这意味着只会在其中一个<app-globals>
上生成更改通知。
What is the recommended way of implementing this pattern in Polymer 1.0? 在Polymer 1.0中实现此模式的推荐方法是什么?
Polymer element <iron-meta>
is also an option. 聚合物元素<iron-meta>
也是一种选择。 For me this was the easiest solution. 对我来说这是最简单的解决方案。
I've extended Etherealones' solution to work as a Behavior, and to extend Polymers "set" and "notifyPath" methods to trigger the updates automatically. 我已经将Etherealones的解决方案扩展为一个行为,并扩展了Polymers“set”和“notifyPath”方法以自动触发更新。 This is as close as i could get to a true databinding across components/elements: 这与我可以跨组件/元素进行真正的数据绑定一样接近:
globals-behavior.html: 全局-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>
And in all polymer elements that should have access to the globals variable: 在所有应该访问全局变量的聚合物元素中:
<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>
I have implemented a pattern like iron-signals
uses for this purpose. 为此目的,我已经实现了像iron-signals
一样的模式。 So the basic principle is that you manually notify other instances when an update occurs. 因此,基本原则是您在更新发生时手动通知其他实例。
Consider this: 考虑一下:
<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>
You will simple call the version that have an underscore suffix like fire_
when you are firing an event. 在触发事件时,您将简单地调用具有下划线后缀的版本,例如fire_
。 You can even create a Polymer Behavior of some sort with this pattern I guess. 您甚至可以使用此模式创建某种类型的聚合物行为。
Beware that preceding underscore properties are already used by Polymer so don't go ahead and convert these to _fire
. 请注意,Polymer已经使用了前面的下划线属性,所以不要继续将它们转换为_fire
。
PS: I didn't look around to solve how to reflect the notification of this.push(array, value);
PS:我没有环顾四周解决如何反映this.push(array, value);
的通知this.push(array, value);
as I don't need it. 因为我不需要它。 I don't know if it's possible this way. 我不知道这是否可能。 Should go find Polymer.Base.push
. 应该去找Polymer.Base.push
。
Sjmiles, one of Polymer's creators just posted the following snippet to the Polymer slack room as an example of shared data: 作为Polymer的创建者之一,Sjmiles刚刚将以下片段发布到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>
I've actually moved my app to using simple data binding, so I'm not sure of the validity of this approach, but maybe it would be useful to someone. 我实际上已经将我的应用程序移动到使用简单的数据绑定,所以我不确定这种方法的有效性,但也许它对某人有用。
I have tried to improve on Alexei Volkov's answer, but I wanted to define the global variables separately. 我试图改进Alexei Volkov的答案,但我想分别定义全局变量。 Instead of the getters/setters I used the observer
property and saved the key together with the instances. 我使用了observer
属性并将密钥与实例一起保存,而不是getter / setter。
The usage is: 用法是:
<app-data key="fName" data="{{firstName}}" ></app-data>
whereas the key
property defines the name of the global variable. 而key
属性定义全局变量的名称。
So for example you can use: 例如,您可以使用:
<!-- 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>
Definition of the <app-data>
dom module: <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>
Fully working demo is here: http://jsbin.com/femaceyusa/1/edit?html,output 完整的工作演示在这里: http : //jsbin.com/femaceyusa/1/edit?html,output
I've combined all suggestions above into the following global polymer object 我已将上述所有建议合并到以下全局聚合物对象中
<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>
and use it elsewere like 并使用它就像是
<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>
Changing either data1.loader or data2.loader affects other instances. 更改data1.loader或data2.loader会影响其他实例。 You should to extend commondata object to add more global properties like it shown with loader property. 您应该扩展commondata对象以添加更多全局属性,如使用loader属性显示的那样。
Using ootwch's solution , I ran into a race condition situation with lazy-loaded components. 使用ootwch的解决方案 ,我遇到了延迟加载组件的竞争条件。
As posted, lazy-loaded components are not initialized with the value from the shared data. 发布后,延迟加载的组件不会使用共享数据中的值进行初始化。
In case anyone else runs into the same problem, I think I fixed it by adding a ready callback like this: 如果其他人遇到同样的问题,我想我通过添加一个像这样的现成回调修复它:
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)
}
},
Hope this saves someone some pain. 希望这能为人们带来一些痛苦。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.