Polymer Dom-重复绑定到内容
Polymer Dom-Repeat Binding to Content
在 polymer 1.2.3 中,dom-repeat
是否可以使用内容作为模板并将值绑定到提供的元素?
自定义元素:
<dom-module id="modify-collection">
<template>
<div>
<template is="dom-repeat" items="[[collection]]">
<content></content>
</template>
</div>
</template>
</dom-module>
用法:
<modify-collection collection="{{things}}">
<list-item resource="[[item]]"></list-item>
</modify-collection>
我在没有帮助的情况下查看了以下资源:
Polymer: How to watch for change in <content> properties
Polymer 1.0 template bind ref equivalent
Data-binding between nested polymer elements
https://github.com/grappendorf/grapp-template-ref
https://github.com/Trakkasure/dom-bindref
https://github.com/Polymer/polymer/issues/1852
https://github.com/Polymer/polymer/pull/2196
2016-02-03 更新:来自 Polymer 团队(PR #2196),计划在未来对此提供更好的支持,以帮助解决一些缺点。
以 iron-list 元素为例。这里它正在抓取传递的模板元素 https://github.com/PolymerElements/iron-list/blob/9909b73a00ecc91fb957232f7bc66b59435d66ad/iron-list.html#L830。
templatizer mixin 用于 AFAIK 来实现对传递模板的绑定(它也被 <template is="dom-repeat">
使用)
TLDR
解决方案 2 虽然有点冗长,但似乎效果最好。它还允许您动态选择模板,也许是基于模型的属性。 (参见解决方案 2:高级示例)
背景故事
这个简单的示例中发生了很多事情,包括模板化、"getting" shadow?、shady 和 light dom 之间的内容,当然还有 data-binding。
这个问题提供了一些见解,但如果没有 Polymer 0.5's injectBoundHtml
,它就不太可行 (Using template defined in light dom inside a Polymer element)。问题是当我们尝试使用 innerHTML 将元素复制到我们的模板时,我们的 data-binding 似乎迷路了(据我所知)。
因此,如果没有它,我们将无法使用 data-bindings 即时创建我们的模板。因此,这两种解决方案都提前将内容包装在模板中;这导致 html 变得惰性,并允许 Polymer 在适当的时间 data-bind (http://www.html5rocks.com/en/tutorials/webcomponents/template/)。
如果你真的想了解所有内容,我建议阅读 Polymer src
for lib/template/dom-repeat.html
、lib/template/templatizer.html
、lib/annotations/annotations.html
(~1500 行)。
解决方案 1 -
请参阅 以获取改进的解决方案 1。
注意,此方法导致 dom-repeat
无法自动呈现内容,因此我们手动调用 render。
元素
<dom-module id="modify-collection">
<template>
<div>
<content></content>
<template id="repeater" is="dom-repeat" items="[[collection]]"></template>
</div>
</template>
<script>
...
ready: function() {
this.$.repeater.templatize(this.querySelector('#templ'));
}
_changeCollection: function(item) {
this.push('collection', item);
this.$.repeater.render();
}
</script>
</dom-module>
用法
<modify-collection collection="{{things}}">
<template id="templ"><list-item resource="[[item]]"></list-item></template>
</modify-collection>
解决方案 2
请注意,此用法与元素 1 不同,因为 <template>
必须具有 is="dom-template"
属性。
辅助元素
(根据此 PR 稍作修改:https://github.com/Polymer/polymer/pull/2196, originally based on https://github.com/grappendorf/grapp-template-ref)
!--
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<!--
Portions of this code have been adapted from the `grapp-template-ref` element.
The original copyright notices are below.
-->
<!--
MIT License
Copyright (c) 2014-2015 Dirk Grappendorf, www.grappendorf.net
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<!--
The `dom-ref` element is a custom `HTMLTemplateElement` type extension that
can be used to reference another template by id using the `ref` property.
`dom-bindref` accepts a `bind` property to bind an object to the referenced
template. By default the bound object can be accessed as `item`, this can be
changed using the `as` property.
Example:
```html
<template is="dom-template" id="template-bind"><span>[[item.key]]</span></template>
<template is="dom-ref" ref="template-bind" bind='{"key":"value"}'></dom-ref>
```
-->
<!-- <link rel="import" href="templatizer.html"> -->
<script>
Polymer({
is: 'dom-ref',
extends: 'template',
/**
* Fired whenever DOM is added or removed by this template (by
* default, rendering occurs lazily). To force immediate rendering, call
* `render`.
*
* @event dom-change
*/
properties: {
/**
* Reference to another template's id.
*/
ref: {
type: String,
observer: '_refChanged'
},
/**
* Object to be bound to referenced template.
*/
bind: {
type: Object,
observer: '_bindChanged'
},
/**
* The name of the variable to add to the binding scope for the
* element associated with a given template instance.
*/
as: {
type: String,
value: 'item'
}
},
behaviors: [
Polymer.Templatizer
],
ready: function() {
this.templatize(this);
},
attached: function() {
return this._stamp();
},
detached: function() {
return this._removeChildren();
},
_refChanged: function(newRef, oldRef) {
if (oldRef) {
this._removeChildren();
return this._stamp();
}
},
_bindChanged: function(newBind, oldBind) {
if (oldBind) {
this._removeChildren();
return this._stamp();
}
},
_stamp: function() {
var root, template, templateRoot, bind = {};
this._parent = Polymer.dom(this).parentNode;
root = this._parent;
while (Polymer.dom(root).parentNode) {
root = Polymer.dom(root).parentNode;
}
template = Polymer.dom(root).querySelector("template#" + this.ref);
// Check For Light Dom Elements that may be passed to this shadow root (Useful for: `<content></content>`)
if (!template) {
template = root.host.querySelector("template#" + this.ref);
}
// Check the whole document
if (!template) {
template = document.querySelector("template#" + this.ref);
}
bind[this.as] = this.bind;
// templateRoot = template.stamp(bind).root;
// Use this method until this lands: https://github.com/Polymer/polymer/pull/1889
templateRoot = (new template.ctor(bind, template)).root;
this._children = Array.prototype.slice.call(templateRoot.childNodes);
return Polymer.dom(this._parent).insertBefore(templateRoot, this);
},
_removeChildren: function() {
var child, i, len, ref, results;
if (this._children) {
ref = this._children;
results = [];
for (i = 0, len = ref.length; i < len; i++) {
child = ref[i];
results.push(Polymer.dom(this._parent).removeChild(child));
}
return results;
}
}
});
</script>
元素
<dom-module id="modify-collection">
<template>
<div>
<content></content>
<template is="dom-repeat" items="[[collection]]">
<template is="dom-ref" bind="[[item]]" ref="templ"></template>
</template>
</div>
</template>
</dom-module>
用法
<modify-collection collection="{{things}}">
<template id="templ" is="dom-template"><list-item resource="[[item]]"></list-item></template>
</modify-collection>
高级元素
上面的用法没有变化。
我们在这里做的是引入一个间接级别,允许我们包装传递给我们元素的模板(在我们的示例中,使用覆盖)。
<dom-module id="modify-collection">
<template>
<div>
<content></content>
<template id="wrapper" is="dom-template">
<div class="overlay">
<template is="dom-ref" bind="[[item]]" ref="templ"></template>
</div>
</template>
<template is="dom-repeat" items="[[collection]]">
<template is="dom-ref" bind="[[item]]" ref="[[_templateRef(_overlayMode)]]"></template>
</template>
</div>
</template>
<script>
...
properties: {
_overlayMode: {
type: Boolean,
value: false
}
},
_templateRef: function(overlayMode) {
return overlayMode ? 'wrapper' : 'templ';
}
</script>
</dom-module>
FWIW,看起来下面的方法和解决方案 1 一样有效,并向其添加了通知路径(和自动 binding/changing 等):
<dom-module id="i-me">
<template>
<content></content>
<template is="dom-repeat" id="repeater" items="[[collection]]"></template>
</template>
<script>
Polymer({
is: 'i-me',
properties: {
collection: {
type: Array,
value: [
{id: 1, name: 'a'},
{id: 2, name: 'b'}
],
notify: true
}
},
ready() {
this.$.repeater.templatize(this.querySelector('#templ'));
Polymer.Bind.prepareModel(this.$.repeater);
Polymer.Base.prepareModelNotifyPath(this.$.repeater);
}
});
</script>
</dom-module>
那么就用这个:
<i-me>
<template id="templ">
<p><span>[[item.id]]</span>: <span>[[item.name]]</span></p>
</template>
</i-me>
在 polymer 1.2.3 中,dom-repeat
是否可以使用内容作为模板并将值绑定到提供的元素?
自定义元素:
<dom-module id="modify-collection">
<template>
<div>
<template is="dom-repeat" items="[[collection]]">
<content></content>
</template>
</div>
</template>
</dom-module>
用法:
<modify-collection collection="{{things}}">
<list-item resource="[[item]]"></list-item>
</modify-collection>
我在没有帮助的情况下查看了以下资源:
Polymer: How to watch for change in <content> properties
Polymer 1.0 template bind ref equivalent
Data-binding between nested polymer elements
https://github.com/grappendorf/grapp-template-ref
https://github.com/Trakkasure/dom-bindref
https://github.com/Polymer/polymer/issues/1852
https://github.com/Polymer/polymer/pull/2196
2016-02-03 更新:来自 Polymer 团队(PR #2196),计划在未来对此提供更好的支持,以帮助解决一些缺点。
以 iron-list 元素为例。这里它正在抓取传递的模板元素 https://github.com/PolymerElements/iron-list/blob/9909b73a00ecc91fb957232f7bc66b59435d66ad/iron-list.html#L830。
templatizer mixin 用于 AFAIK 来实现对传递模板的绑定(它也被 <template is="dom-repeat">
使用)
TLDR
解决方案 2 虽然有点冗长,但似乎效果最好。它还允许您动态选择模板,也许是基于模型的属性。 (参见解决方案 2:高级示例)
背景故事
这个简单的示例中发生了很多事情,包括模板化、"getting" shadow?、shady 和 light dom 之间的内容,当然还有 data-binding。
这个问题提供了一些见解,但如果没有 Polymer 0.5's injectBoundHtml
,它就不太可行 (Using template defined in light dom inside a Polymer element)。问题是当我们尝试使用 innerHTML 将元素复制到我们的模板时,我们的 data-binding 似乎迷路了(据我所知)。
因此,如果没有它,我们将无法使用 data-bindings 即时创建我们的模板。因此,这两种解决方案都提前将内容包装在模板中;这导致 html 变得惰性,并允许 Polymer 在适当的时间 data-bind (http://www.html5rocks.com/en/tutorials/webcomponents/template/)。
如果你真的想了解所有内容,我建议阅读 Polymer src
for lib/template/dom-repeat.html
、lib/template/templatizer.html
、lib/annotations/annotations.html
(~1500 行)。
解决方案 1 -
请参阅
注意,此方法导致 dom-repeat
无法自动呈现内容,因此我们手动调用 render。
元素
<dom-module id="modify-collection">
<template>
<div>
<content></content>
<template id="repeater" is="dom-repeat" items="[[collection]]"></template>
</div>
</template>
<script>
...
ready: function() {
this.$.repeater.templatize(this.querySelector('#templ'));
}
_changeCollection: function(item) {
this.push('collection', item);
this.$.repeater.render();
}
</script>
</dom-module>
用法
<modify-collection collection="{{things}}">
<template id="templ"><list-item resource="[[item]]"></list-item></template>
</modify-collection>
解决方案 2
请注意,此用法与元素 1 不同,因为 <template>
必须具有 is="dom-template"
属性。
辅助元素
(根据此 PR 稍作修改:https://github.com/Polymer/polymer/pull/2196, originally based on https://github.com/grappendorf/grapp-template-ref)
!--
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<!--
Portions of this code have been adapted from the `grapp-template-ref` element.
The original copyright notices are below.
-->
<!--
MIT License
Copyright (c) 2014-2015 Dirk Grappendorf, www.grappendorf.net
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<!--
The `dom-ref` element is a custom `HTMLTemplateElement` type extension that
can be used to reference another template by id using the `ref` property.
`dom-bindref` accepts a `bind` property to bind an object to the referenced
template. By default the bound object can be accessed as `item`, this can be
changed using the `as` property.
Example:
```html
<template is="dom-template" id="template-bind"><span>[[item.key]]</span></template>
<template is="dom-ref" ref="template-bind" bind='{"key":"value"}'></dom-ref>
```
-->
<!-- <link rel="import" href="templatizer.html"> -->
<script>
Polymer({
is: 'dom-ref',
extends: 'template',
/**
* Fired whenever DOM is added or removed by this template (by
* default, rendering occurs lazily). To force immediate rendering, call
* `render`.
*
* @event dom-change
*/
properties: {
/**
* Reference to another template's id.
*/
ref: {
type: String,
observer: '_refChanged'
},
/**
* Object to be bound to referenced template.
*/
bind: {
type: Object,
observer: '_bindChanged'
},
/**
* The name of the variable to add to the binding scope for the
* element associated with a given template instance.
*/
as: {
type: String,
value: 'item'
}
},
behaviors: [
Polymer.Templatizer
],
ready: function() {
this.templatize(this);
},
attached: function() {
return this._stamp();
},
detached: function() {
return this._removeChildren();
},
_refChanged: function(newRef, oldRef) {
if (oldRef) {
this._removeChildren();
return this._stamp();
}
},
_bindChanged: function(newBind, oldBind) {
if (oldBind) {
this._removeChildren();
return this._stamp();
}
},
_stamp: function() {
var root, template, templateRoot, bind = {};
this._parent = Polymer.dom(this).parentNode;
root = this._parent;
while (Polymer.dom(root).parentNode) {
root = Polymer.dom(root).parentNode;
}
template = Polymer.dom(root).querySelector("template#" + this.ref);
// Check For Light Dom Elements that may be passed to this shadow root (Useful for: `<content></content>`)
if (!template) {
template = root.host.querySelector("template#" + this.ref);
}
// Check the whole document
if (!template) {
template = document.querySelector("template#" + this.ref);
}
bind[this.as] = this.bind;
// templateRoot = template.stamp(bind).root;
// Use this method until this lands: https://github.com/Polymer/polymer/pull/1889
templateRoot = (new template.ctor(bind, template)).root;
this._children = Array.prototype.slice.call(templateRoot.childNodes);
return Polymer.dom(this._parent).insertBefore(templateRoot, this);
},
_removeChildren: function() {
var child, i, len, ref, results;
if (this._children) {
ref = this._children;
results = [];
for (i = 0, len = ref.length; i < len; i++) {
child = ref[i];
results.push(Polymer.dom(this._parent).removeChild(child));
}
return results;
}
}
});
</script>
元素
<dom-module id="modify-collection">
<template>
<div>
<content></content>
<template is="dom-repeat" items="[[collection]]">
<template is="dom-ref" bind="[[item]]" ref="templ"></template>
</template>
</div>
</template>
</dom-module>
用法
<modify-collection collection="{{things}}">
<template id="templ" is="dom-template"><list-item resource="[[item]]"></list-item></template>
</modify-collection>
高级元素
上面的用法没有变化。
我们在这里做的是引入一个间接级别,允许我们包装传递给我们元素的模板(在我们的示例中,使用覆盖)。
<dom-module id="modify-collection">
<template>
<div>
<content></content>
<template id="wrapper" is="dom-template">
<div class="overlay">
<template is="dom-ref" bind="[[item]]" ref="templ"></template>
</div>
</template>
<template is="dom-repeat" items="[[collection]]">
<template is="dom-ref" bind="[[item]]" ref="[[_templateRef(_overlayMode)]]"></template>
</template>
</div>
</template>
<script>
...
properties: {
_overlayMode: {
type: Boolean,
value: false
}
},
_templateRef: function(overlayMode) {
return overlayMode ? 'wrapper' : 'templ';
}
</script>
</dom-module>
FWIW,看起来下面的方法和解决方案 1 一样有效,并向其添加了通知路径(和自动 binding/changing 等):
<dom-module id="i-me">
<template>
<content></content>
<template is="dom-repeat" id="repeater" items="[[collection]]"></template>
</template>
<script>
Polymer({
is: 'i-me',
properties: {
collection: {
type: Array,
value: [
{id: 1, name: 'a'},
{id: 2, name: 'b'}
],
notify: true
}
},
ready() {
this.$.repeater.templatize(this.querySelector('#templ'));
Polymer.Bind.prepareModel(this.$.repeater);
Polymer.Base.prepareModelNotifyPath(this.$.repeater);
}
});
</script>
</dom-module>
那么就用这个:
<i-me>
<template id="templ">
<p><span>[[item.id]]</span>: <span>[[item.name]]</span></p>
</template>
</i-me>