聚合物 class 内部绑定 dom-重复无效
polymer class binding inside dom-repeat not working
我正在尝试将计算值绑定到 dom-repeat 模板中的元素。
<iron-selector class="dropdown-content" selected="0">
<template is="dom-repeat" items="[[items]]">
<div class$="item {{_itemClass(selectedItems)}}" data-value="[[item]]" on-click="_onItemClick">[[item]]</div>
</template>
</iron-selector>
我的目标是在用户选择其中一项时更新下拉项样式。发生这种情况时,我通过将项目值推送到数组来更新 selectedItems
数组。这将触发选择框的渲染,但将无法调用 _itemClass(selectedItems)
表达式。
我在这里错过了什么?我在文档中读到,您必须指定将触发绑定的属性,我通过将 selectedItems
添加到绑定表达式来做到这一点,但完全没有运气。
我附上了当前行为的片段。
欢迎任何帮助。
<!doctype html>
<head>
<meta charset="utf-8">
<!-- polygit -->
<base href="https://polygit.org/components/"> <!-- saves typing! -->
<script src="webcomponentsjs/webcomponents-lite.min.js"></script>
<link rel="import" href="iron-flex-layout/iron-flex-layout-classes.html">
<link rel="import" href="iron-icons/iron-icons.html">
<link rel="import" href="iron-dropdown/iron-dropdown.html">
<link rel="import" href="iron-selector/iron-selector.html">
<!-- style -->
<style>
x-example {
width: 250px;
}
</style>
</head>
<body>
<x-example></x-example>
<dom-module id="x-example">
<style include="iron-flex iron-flex-alignment"></style>
<style>
:host {
position: relative;
border: 1px solid blue;
display: block;
}
.body {
@apply --layout-horizontal;
}
.body .selection {
border: 1px solid green;
margin: 2px;
flex-grow: 1
}
.body .selection .item {
display: inline-block;
margin: 2px;
background: lightgreen;
padding: 2px 5px;
border: 1px solid green;
border-radius: 4px;
color: darkblue;
}
.body .selection .item .text {
display: inline-block;
}
.body .selection .item iron-icon {
width: 18px;
cursor: pointer;
}
.body .dropdown {
border: 1px solid yellow;
margin: 2px;
cursor: pointer;
min-height: 40px;
}
.body .dropdown iron-icon {
margin: auto;
}
.options iron-dropdown {
border: 1px solid grey;
border-radius: 6px;
overflow: hidden;
}
.options .item {
cursor: pointer;
}
iron-selector>* {
padding: 8px;
background: aqua;
}
.horizontal-section {
padding: 0;
}
.iron-selected {
background-color: var(--google-blue-500);
color: white;
}
</style>
<template>
<div class="body">
<div class="flex selection">
<template is="dom-repeat" items="[[selectedItems]]">
<div class="item">
<div class="text">[[item]]</div>
<iron-icon class="dropdown-trigger" icon="icons:clear" on-click="_onRemoveItem"></iron-icon>
</div>
</template>
</div>
<div class="dropdown" on-click="_toggleDropdown">
<iron-icon icon="icons:arrow-drop-down"></iron-icon>
</div>
</div>
<div class="options">
<iron-dropdown auto-fit-on-attach="true" id="dropdown" horizontal-align="left" vertical-align="top">
<iron-selector class="dropdown-content" selected="0">
<template is="dom-repeat" items="[[items]]">
<div class$="item {{_itemClass(selectedItems)}}" data-value="[[item]]" on-click="_onItemClick">[[item]]</div>
</template>
</iron-selector>
</iron-dropdown>
</div>
</template>
<script>
// only need this when both (1) in the main document and (2) on non-Chrome browsers
addEventListener('WebComponentsReady', function() {
Polymer({
is: "x-example",
properties: {
items: {
type: Array,
value: ["One", "Two", "Three"]
},
selectedItems: {
type: Array,
value: []
}
},
_toggleDropdown: function() {
this.$.dropdown.toggle();
},
_onItemClick: function(e) {
var value = e.srcElement.dataValue;
if (this.selectedItems.indexOf(value) >= 0) {
return;
}
this.push("selectedItems", value)
this._refreshDropdown();
},
_refreshDropdown: function() {
this.async(function() {
this.$.dropdown.position();
})
},
_onRemoveItem(e) {
let index = this.selectedItems.indexOf(e.srcElement.dataValue);
this.splice("selectedItems", index, 1);
this._refreshDropdown();
},
_itemClass: function() {
console.log("item class called")
}
});
});
</script>
</dom-module>
</body>
我没有测试过你的问题,但我自己也在使用类似的模式。我的猜测是 Polymer 不支持在同一标签内声明 class 和绑定 class。
我改用属性和属性选择器,效果和预期的一样。
能否尝试将 class 更改为基于属性选择器的属性和样式?相关代码如下:
<style>
.item[selected] {
font-weight: bold;
}
</style>
...
<template is="dom-repeat" items="[[items]]">
<div class="item" selected$="[[_computeSelected]]" on-tap="_onItemTap">[[item]]</div>
</template>
_onItemTap: function(event) {
const item = event.model.item;
},
_computeSelected: function(item) (
return yourSelectionLogic ? true : false;
}
另外两条注意事项:使用点击而不是点击。它使移动触摸事件与桌面事件规范化。此外,您不需要为每个项目绑定数据值,因为它是在 dom-repeat.
中触发时通过点击事件包含的。
您的绑定是 _itemClass(selectedItems)
,它仅在变量 selectedItems 更改时触发,而不是在数组发生变化时触发。
您有两个选择:
1) 将绑定更改为 _itemClass(selectedItems.*)
,这将由 this.push
和其他数组变异方法触发
2)每次更改变量:
this.set('selectedItems', [].concat(this.selectedItems).concat(value));
第一个解决方案(第二个解决方案被注释掉)的工作示例可用here
我正在尝试将计算值绑定到 dom-repeat 模板中的元素。
<iron-selector class="dropdown-content" selected="0">
<template is="dom-repeat" items="[[items]]">
<div class$="item {{_itemClass(selectedItems)}}" data-value="[[item]]" on-click="_onItemClick">[[item]]</div>
</template>
</iron-selector>
我的目标是在用户选择其中一项时更新下拉项样式。发生这种情况时,我通过将项目值推送到数组来更新 selectedItems
数组。这将触发选择框的渲染,但将无法调用 _itemClass(selectedItems)
表达式。
我在这里错过了什么?我在文档中读到,您必须指定将触发绑定的属性,我通过将 selectedItems
添加到绑定表达式来做到这一点,但完全没有运气。
我附上了当前行为的片段。
欢迎任何帮助。
<!doctype html>
<head>
<meta charset="utf-8">
<!-- polygit -->
<base href="https://polygit.org/components/"> <!-- saves typing! -->
<script src="webcomponentsjs/webcomponents-lite.min.js"></script>
<link rel="import" href="iron-flex-layout/iron-flex-layout-classes.html">
<link rel="import" href="iron-icons/iron-icons.html">
<link rel="import" href="iron-dropdown/iron-dropdown.html">
<link rel="import" href="iron-selector/iron-selector.html">
<!-- style -->
<style>
x-example {
width: 250px;
}
</style>
</head>
<body>
<x-example></x-example>
<dom-module id="x-example">
<style include="iron-flex iron-flex-alignment"></style>
<style>
:host {
position: relative;
border: 1px solid blue;
display: block;
}
.body {
@apply --layout-horizontal;
}
.body .selection {
border: 1px solid green;
margin: 2px;
flex-grow: 1
}
.body .selection .item {
display: inline-block;
margin: 2px;
background: lightgreen;
padding: 2px 5px;
border: 1px solid green;
border-radius: 4px;
color: darkblue;
}
.body .selection .item .text {
display: inline-block;
}
.body .selection .item iron-icon {
width: 18px;
cursor: pointer;
}
.body .dropdown {
border: 1px solid yellow;
margin: 2px;
cursor: pointer;
min-height: 40px;
}
.body .dropdown iron-icon {
margin: auto;
}
.options iron-dropdown {
border: 1px solid grey;
border-radius: 6px;
overflow: hidden;
}
.options .item {
cursor: pointer;
}
iron-selector>* {
padding: 8px;
background: aqua;
}
.horizontal-section {
padding: 0;
}
.iron-selected {
background-color: var(--google-blue-500);
color: white;
}
</style>
<template>
<div class="body">
<div class="flex selection">
<template is="dom-repeat" items="[[selectedItems]]">
<div class="item">
<div class="text">[[item]]</div>
<iron-icon class="dropdown-trigger" icon="icons:clear" on-click="_onRemoveItem"></iron-icon>
</div>
</template>
</div>
<div class="dropdown" on-click="_toggleDropdown">
<iron-icon icon="icons:arrow-drop-down"></iron-icon>
</div>
</div>
<div class="options">
<iron-dropdown auto-fit-on-attach="true" id="dropdown" horizontal-align="left" vertical-align="top">
<iron-selector class="dropdown-content" selected="0">
<template is="dom-repeat" items="[[items]]">
<div class$="item {{_itemClass(selectedItems)}}" data-value="[[item]]" on-click="_onItemClick">[[item]]</div>
</template>
</iron-selector>
</iron-dropdown>
</div>
</template>
<script>
// only need this when both (1) in the main document and (2) on non-Chrome browsers
addEventListener('WebComponentsReady', function() {
Polymer({
is: "x-example",
properties: {
items: {
type: Array,
value: ["One", "Two", "Three"]
},
selectedItems: {
type: Array,
value: []
}
},
_toggleDropdown: function() {
this.$.dropdown.toggle();
},
_onItemClick: function(e) {
var value = e.srcElement.dataValue;
if (this.selectedItems.indexOf(value) >= 0) {
return;
}
this.push("selectedItems", value)
this._refreshDropdown();
},
_refreshDropdown: function() {
this.async(function() {
this.$.dropdown.position();
})
},
_onRemoveItem(e) {
let index = this.selectedItems.indexOf(e.srcElement.dataValue);
this.splice("selectedItems", index, 1);
this._refreshDropdown();
},
_itemClass: function() {
console.log("item class called")
}
});
});
</script>
</dom-module>
</body>
我没有测试过你的问题,但我自己也在使用类似的模式。我的猜测是 Polymer 不支持在同一标签内声明 class 和绑定 class。
我改用属性和属性选择器,效果和预期的一样。
能否尝试将 class 更改为基于属性选择器的属性和样式?相关代码如下:
<style>
.item[selected] {
font-weight: bold;
}
</style>
...
<template is="dom-repeat" items="[[items]]">
<div class="item" selected$="[[_computeSelected]]" on-tap="_onItemTap">[[item]]</div>
</template>
_onItemTap: function(event) {
const item = event.model.item;
},
_computeSelected: function(item) (
return yourSelectionLogic ? true : false;
}
另外两条注意事项:使用点击而不是点击。它使移动触摸事件与桌面事件规范化。此外,您不需要为每个项目绑定数据值,因为它是在 dom-repeat.
中触发时通过点击事件包含的。您的绑定是 _itemClass(selectedItems)
,它仅在变量 selectedItems 更改时触发,而不是在数组发生变化时触发。
您有两个选择:
1) 将绑定更改为 _itemClass(selectedItems.*)
,这将由 this.push
和其他数组变异方法触发
2)每次更改变量:
this.set('selectedItems', [].concat(this.selectedItems).concat(value));
第一个解决方案(第二个解决方案被注释掉)的工作示例可用here