2017-05-22 154 views
5

我正在尝试构建呈现JSON对象集合的通用Web组件,如树视图和多列表视图(在两个列表之间移动项目)。我想复制iron-list使用的模式,其中包含单个项目演示文稿的模板被传递到组件以供重用。聚合物2动态合并模板

例如,假设该Web组件的模板:

<dom-module id="intworkspace-tree"> 
 
    <template> 
 
    <style include="iron-flex iron-flex-alignment"> 
 

 
     paper-icon-item { 
 
     --paper-item-min-height: var(--intworkspace-tree-margin,30px); 
 
     --paper-item-icon-width : var(--intworkspace-tree-margin,30px); 
 
     } 
 

 
     paper-icon-item:focus::before, 
 
     paper-icon-item:focus::after { 
 
     color: inherit; 
 
     opacity: 0; 
 
     } 
 

 
    .node { 
 
     margin-left: var(--intworkspace-tree-margin,30px);; 
 
     } 
 
    </style> 
 

 
    <slot id="labelView"></slot> 
 

 
    <template id="nodeView"> 
 
     <div class="layout vertical"> 
 
     <paper-icon-item on-tap="nodeSelected"> 
 
     <iron-icon icon="expand-less" slot="item-icon" hidden$="[[!hasNodes(node)]]"></iron-icon> 
 
     <!-- label goes here--> 
 
     </paper-icon-item> 
 

 
     <iron-collapse class="node" opened hidden$="[[!hasNodes(node)]]"> 
 
     <intworkspace-tree tree="[[node.nodes]]" embedded></intworkspace-tree> 
 
     </iron-collapse> 
 
     </div> 
 
    </template> 
 

 
    </template> 
 
\t ... 
 
    </dom-module>

这种用法:

<intworkspace-tree tree="{{testTree}}"> 
 
     <template><paper-item-body>[[node.name]]</paper-item-body> </template> 
 
    </intworkspace-tree> 
 
我想呈现JSON树阵中将web组件的模板结合起来的层次结构使用通过插槽提供的模板渲染不透明的JSON对象。到目前为止,我已经确定了结合模板的方法有两种:

  1. 利用Polymer.Templatize.templatize API来加载模板,创建/邮票新实例,并使用DOM API一起追加它们并将它们添加到Web组件的影子DOM。

  2. 访问模板内容,将它们组合在一起,创建并导入新模板,然后根据需要克隆它。

在经历了很多逆境之后,我成功地实现了#1而不是#2,这是我的问题的动机。 #2更吸引我,因为我一次合并模板比合并它们生成的加盖实例更容易,这种方法似乎是我可以重复使用像dom-repeat这样的嵌套模板的唯一方法。

我的主要障碍是,一旦聚合物或可能是polyfill加载模板变得不透明,只能通过聚合物templatize功能使用。例如,该代码工作正常,没有任何进口聚合物:

<template> 
 
    <div>Template Contents</div> 
 
</template> 
 
<div> 
 
    Template Test 
 
</div> 
 
    <script> 
 
    let template = document.querySelector("template"); 
 
    let clone = document.importNode(template.content,true); 
 
    document.querySelector("div").appendChild(clone); 
 
    </script>

聚合物外template.content DOMFragment有儿童和innerHTML的设置。但是,一旦使用聚合物,template.content就没有孩子,innerHTML也是空的。这使我从使用DOM API来创建混合可用模板一起新的模板,即

let newTemplate = document.createElement("template"); 
 
newTemplate.content = ... // combine #labelView > template.content with #nodeView.content 
 
let nodeView = document.importNode(newTemplate.content,true); 
 
nodeView.tree=...

也许通过使用标准的HTML机制,我没有工作,设计导入模板。有没有另一种方法来在运行时用Polymer动态创建/合并模板?我的主要动机是,我想重新使用嵌套在模板中的dom-if和dom-repeat Web组件,而不必重新实现其所有功能。

回答

4

经过进一步的研究,我发现了聚合物2的三个特征。0,使我能够产生令人满意的解决方案:

  1. 每当Polymer处理DOM模板时,默认情况下它会记忆它们。此模板缓存可防止费用克隆操作并简化模板绑定。 Polymer 2.0 DOM模板文档解释说,preserve-content属性可以添加到模板中以绕过优化,从而允许使用本机DOM操作来操作模板。

  2. DOM模板文档还介绍了获取自定义元素的原始模板的多种方法。一种选择是调用元素的静态模板()方法,另一种选择是使用Polymer.DomModule.import()函数。第二种方法对我很感兴趣,因为它允许管理超出默认模块模板的多个模板。

  3. Polymer.TemplateStamp API类具有内部_stampTemplate()函数,该函数用于将模板戳到自定义元素的DOM中。我宁愿使用记录良好的Polymer.Templatize.templatize()函数,但它会在模板本身上查找属性和方法,在我的情况下,它不是定义了行为的自定义元素。

把这三个特征我一起能够以制备可重复使用的动态合并模板utlizing嵌套DOM-IFS和作为我希望的DOM的重复。

下面是功能性结果:

组件:

<link rel="import" href="../polymer/polymer-element.html"> 
 
<link rel="import" href="../iron-collapse/iron-collapse.html"> 
 
<link rel="import" href="../paper-item/paper-icon-item.html"> 
 
<link rel="import" href="../paper-item/paper-item-body.html"> 
 
<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-icon/iron-icon.html"> 
 

 

 
<dom-module id="intworkspace-tree"> 
 
    <template> 
 
    <!-- style includes don't work in stamped template, only in the shadowRoot --> 
 
    <style include="iron-flex iron-flex-alignment"> 
 

 
    paper-icon-item { 
 
     --paper-item-min-height: var(--intworkspace-tree-margin,30px); 
 
     --paper-item-icon-width : var(--intworkspace-tree-margin,30px); 
 
    } 
 

 
    paper-icon-item:focus::before, 
 
    paper-icon-item:focus::after { 
 
     color: inherit; 
 
     opacity: 0; 
 
    } 
 

 
    .node { 
 
     margin-left: var(--intworkspace-tree-margin,30px);; 
 
    } 
 
    </style> 
 

 
    <slot id="labelView"></slot> 
 
    </template> 
 

 
    <template id="nodeView"> 
 

 
    
 

 
    <template is="dom-repeat" items="{{tree}}" as="node" index-as="n"> 
 
     <div class="layout vertical"> 
 
      <!--<div>index: [[n]]</div> 
 
      <div>name: [[node.name]]</div>--> 
 
      <paper-icon-item on-tap="nodeSelected"> 
 
      <template is="dom-if" if="[[hasNodes(node)]]"> 
 
       <iron-icon icon="expand-more" slot="item-icon" hidden$="[[!hasNodes(node)]]"></iron-icon> 
 
      </template> 
 
      <!-- label goes here--> 
 
      </paper-icon-item> 
 
      <template is="dom-if" if="[[hasNodes(node)]]"> 
 
      <iron-collapse class="node" opened> 
 
       <intworkspace-tree tree="[[node.nodes]]" node-template="[[nodeTemplate]]" embedded></intworkspace-tree> 
 
      </iron-collapse> 
 
      </template> 
 
     </div> 
 
    </template> 
 
    </template> 
 

 
    <script> 
 
    class IntTree extends Polymer.TemplateStamp(Polymer.Element) { 
 

 
     static get is() { 
 
     return 'intworkspace-tree'; 
 
     } 
 

 
     static get properties() { 
 
     return { 
 
      tree: { 
 
      type: Array, 
 
      value: [] 
 
      }, 
 
      nodeTemplate: { 
 
      type: Object, 
 
      } 
 
     }; 
 
     } 
 

 
     ready() { 
 
     super.ready(); 
 
     if (!this.hasAttribute("embedded")) { 
 
      let labelTemplate = this.$.labelView.assignedNodes().find((e) => { 
 
      return e instanceof HTMLTemplateElement; 
 
      }); 
 
      let nodeTemplate = document.importNode(Polymer.DomModule.import(IntTree.is, "#nodeView"), true); 
 
      let repeatTemplate = nodeTemplate.content.querySelector("template[is='dom-repeat']"); 
 
      let iconItem = repeatTemplate.content.querySelector('paper-icon-item'); 
 
      iconItem.appendChild(labelTemplate.content); 
 
      this.nodeTemplate = nodeTemplate; 
 
     } 
 
     let nodeInstance = this._stampTemplate(this.nodeTemplate); 
 
     this.shadowRoot.appendChild(nodeInstance); 
 
     } 
 

 
     hasNodes(node) { 
 
     return node.nodes != null && node.nodes.length > 0; 
 
     } 
 

 
     nodeSelected(e) { 
 
     let collapse = e.currentTarget.parentNode.querySelector("iron-collapse"); 
 
     let nodeIcon = e.currentTarget.parentNode.querySelector("iron-icon"); 
 
     if (collapse && nodeIcon) { 
 
      collapse.toggle(); 
 
      if (collapse.opened) { 
 
      nodeIcon.icon = "expand-more"; 
 
      } else { 
 
      nodeIcon.icon = "expand-less"; 
 
      } 
 
     } 
 
     } 
 
    } 
 

 
    window.customElements.define(IntTree.is, IntTree); 
 
    </script> 
 
</dom-module>

用法:

<intworkspace-tree tree="{{testTree}}"> 
 
     <template preserve-content><paper-item-body>[[node.name]]</paper-item-body></template> 
 
    </intworkspace-tree>

0

我在这里给Aaron的解决方案增加了一个观察,因为我没有足够的声望来添加评论。

注意:此行有双重进口

let nodeTemplate = document.importNode(Polymer.DomModule.import(IntTree.is, "#nodeView"), true); 

这是没有必要的。在铬和Safari浏览器出于某种原因,但不是在FF。

因此,与聚合物的工作,只是用DomModule进口足够

let nodeTemplate = Polymer.DomModule.import(IntTree.is, '#nodeView'); 

希望这有助于有人