聚合物数据绑定到属性的奇怪行为
Strange behavior in Polymer data-binding to an attribute
我正在使用 Polymer 1.0 尝试绑定到自定义元素的属性,然后只显示它。
自定义元素实际上是一个 <iron-input>
列表,它有一个添加按钮和一个删除按钮。我想将该列表中的任何更改反映给主持人。它还有一个 minItemSize
属性,这意味着它至少有这么多元素。所以我向观察者添加了一个检查,添加了额外的元素以防它低于这个数字。
但是当我绑定到包含列表的属性时,事情变得不同步,我可以从 ui.
中删除所有输入
我有两个 <dyn-inputlist>
元素。在其中一个中,我没有绑定到 data
属性,另一个我做。
第一个行为符合预期:在单击按钮时添加和删除。
另一个不行,因为你可以去掉所有的输入框。尽管数据本身已更新,并填充了额外的项目,但出于某种原因 UI 并未反映这一点。 (检查元素的数据 属性 确实表明它具有正确的项目数)
我还希望如果我在两个 dyn-inputlist
元素上设置 data={{myData}}
,它们总是显示相同的内容。但是随机按任一组件上的 add/remove 按钮会使它们不同步。
我错过了什么吗?
提前致谢。
index.html
:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script src="bower_components/webcomponentsjs/webcomponents.js"></script>
<link rel="import" href="components/dyn-inputlist.html"/>
</head>
<body>
<template is="dom-bind">
<dyn-inputlist min-item-size="4"></dyn-inputlist>
<div>{{mydata}}</div>
<dyn-inputlist min-item-size="4" data="{{mydata}}"></dyn-inputlist>
</template>
</body>
</html>
dyn-inputlist.html
:
<link rel="import" href="../../polymer/polymer.html">
<link rel="import" href="../../iron-input/iron-input.html">
<dom-module id="dyn-inputlist">
<template>
<button on-click="removeItem">x</button>
<button on-click="addItem">+</button>
<template is="dom-repeat" items="{{data}}">
<div>
<span>{{index}}</span>
<input is="iron-input" bind-value="{{item.content}}">
</div>
</template>
</template>
<script>
Polymer({
is: 'dyn-inputlist',
properties: {
minItemSize: {
type: Number,
notify: true,
value: 1
},
data: {
type: Array,
reflectToAttribute: true,
notify: true,
value: function () {
return []
}
}
},
observers: ['_dataChanged(data.*)'],
addItem: function (e) {
this.unshift('data', {content: ""});
this.reflectPropertyToAttribute('data')
},
removeItem: function (e) {
this.shift('data');
this.reflectPropertyToAttribute('data')
},
_dataChanged: function (e) {
if (this.data != null) {
while (this.data.length < this.minItemSize) {
this.push('data', {content: ""})
}
} else {
this.data = [{content: ""}];
}
this.reflectPropertyToAttribute('data');
}
});
</script>
</dom-module>
编辑:
这是实时代码:http://jsbin.com/poquke/1/edit?html,output
我试了一下你的代码,我注意到如果你将代码包装在你更改过的处理程序中的异步函数中,它就会起作用。这解决了您描述的两个问题。
_dataChanged: function (e) {
this.async(function(){
if (this.data != null) {
while (this.data.length < this.minItemSize) {
this.push('data', {content: ""})
}
} else {
this.data = [{content: ""}];
}
});
}
对于这种行为,我没有一个完美的解释。我假设它与 Polymer 处理变化观察的方式有关。每次您推送到已更改处理程序中的数据数组时,这实际上会更改数据并且应该再次触发处理程序。
简化的话就不需要async了。
这是简化的代码,这消除了当您推送最小值时对 _dataChanged 的重复调用,并允许 polymer 的内置事件系统负责更新和通知其他元素。函数:_createNewItem() 用于创建对象。这简化了处理项目对象创建的位置。
http://jsbin.com/vemita/6/edit?html,output
link 和 URL 引用已从上述问题中的示例代码更改为符合与 polyserve 一起使用的聚合物元素和演示页面标准。
我已经对您的原始代码进行了评论,以了解为什么每一行都应该或不应该在那里。这包括更改 _dataChanged
的原因
我正在使用 Polymer 1.0 尝试绑定到自定义元素的属性,然后只显示它。
自定义元素实际上是一个 <iron-input>
列表,它有一个添加按钮和一个删除按钮。我想将该列表中的任何更改反映给主持人。它还有一个 minItemSize
属性,这意味着它至少有这么多元素。所以我向观察者添加了一个检查,添加了额外的元素以防它低于这个数字。
但是当我绑定到包含列表的属性时,事情变得不同步,我可以从 ui.
中删除所有输入我有两个 <dyn-inputlist>
元素。在其中一个中,我没有绑定到 data
属性,另一个我做。
第一个行为符合预期:在单击按钮时添加和删除。
另一个不行,因为你可以去掉所有的输入框。尽管数据本身已更新,并填充了额外的项目,但出于某种原因 UI 并未反映这一点。 (检查元素的数据 属性 确实表明它具有正确的项目数)
我还希望如果我在两个 dyn-inputlist
元素上设置 data={{myData}}
,它们总是显示相同的内容。但是随机按任一组件上的 add/remove 按钮会使它们不同步。
我错过了什么吗? 提前致谢。
index.html
:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script src="bower_components/webcomponentsjs/webcomponents.js"></script>
<link rel="import" href="components/dyn-inputlist.html"/>
</head>
<body>
<template is="dom-bind">
<dyn-inputlist min-item-size="4"></dyn-inputlist>
<div>{{mydata}}</div>
<dyn-inputlist min-item-size="4" data="{{mydata}}"></dyn-inputlist>
</template>
</body>
</html>
dyn-inputlist.html
:
<link rel="import" href="../../polymer/polymer.html">
<link rel="import" href="../../iron-input/iron-input.html">
<dom-module id="dyn-inputlist">
<template>
<button on-click="removeItem">x</button>
<button on-click="addItem">+</button>
<template is="dom-repeat" items="{{data}}">
<div>
<span>{{index}}</span>
<input is="iron-input" bind-value="{{item.content}}">
</div>
</template>
</template>
<script>
Polymer({
is: 'dyn-inputlist',
properties: {
minItemSize: {
type: Number,
notify: true,
value: 1
},
data: {
type: Array,
reflectToAttribute: true,
notify: true,
value: function () {
return []
}
}
},
observers: ['_dataChanged(data.*)'],
addItem: function (e) {
this.unshift('data', {content: ""});
this.reflectPropertyToAttribute('data')
},
removeItem: function (e) {
this.shift('data');
this.reflectPropertyToAttribute('data')
},
_dataChanged: function (e) {
if (this.data != null) {
while (this.data.length < this.minItemSize) {
this.push('data', {content: ""})
}
} else {
this.data = [{content: ""}];
}
this.reflectPropertyToAttribute('data');
}
});
</script>
</dom-module>
编辑: 这是实时代码:http://jsbin.com/poquke/1/edit?html,output
我试了一下你的代码,我注意到如果你将代码包装在你更改过的处理程序中的异步函数中,它就会起作用。这解决了您描述的两个问题。
_dataChanged: function (e) {
this.async(function(){
if (this.data != null) {
while (this.data.length < this.minItemSize) {
this.push('data', {content: ""})
}
} else {
this.data = [{content: ""}];
}
});
}
对于这种行为,我没有一个完美的解释。我假设它与 Polymer 处理变化观察的方式有关。每次您推送到已更改处理程序中的数据数组时,这实际上会更改数据并且应该再次触发处理程序。
简化的话就不需要async了。
这是简化的代码,这消除了当您推送最小值时对 _dataChanged 的重复调用,并允许 polymer 的内置事件系统负责更新和通知其他元素。函数:_createNewItem() 用于创建对象。这简化了处理项目对象创建的位置。
http://jsbin.com/vemita/6/edit?html,output
link 和 URL 引用已从上述问题中的示例代码更改为符合与 polyserve 一起使用的聚合物元素和演示页面标准。
我已经对您的原始代码进行了评论,以了解为什么每一行都应该或不应该在那里。这包括更改 _dataChanged
的原因