Polymer 2动态合并模板 [英] Polymer 2 dynamically merging templates

查看:65
本文介绍了Polymer 2动态合并模板的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试构建呈现JSON对象集合的通用Web组件,例如树视图和多列表视图(在两个列表之间移动项目).我想复制铁表使用的模式,其中将包含单个项目演示的模板传递到组件中以供重用.

I am attempting to build generic web components that render JSON object collections, like a tree view and a multi-list view (moving items between two lists). I would like to copy the pattern used by iron-list where a template containing the individual item presentation is passed into the component for reuse.

例如,给定此Web组件模板:

For example, given this web component template:

<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>
	...
  </dom-module>

和这种用法:

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

我想在一个层次结构中呈现JSON树数组,该结构结合了Web组件的模板以及通过插槽提供的模板,以呈现不透明的JSON对象.到目前为止,我已经确定了两种组合模板的方法:

I would like to render the JSON tree array in a hierachy that combines the web component's template along with template provided through the slot to render the opaque JSON objects. So far I have identified two methods of combining the templates:

  1. 使用Polymer.Templatize.templatize API加载模板,创建/标记新实例,并使用DOM API将它们附加在一起并将其添加到Web组件的影子DOM.

  1. Utilize the Polymer.Templatize.templatize API to load the templates, create/stamp new instances, and use the DOM API to append them together and add them to the web component's shadow DOM.

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

Access the templates contents, combine them together, create and import a new template, and then clone it as needed.

在经历了许多逆境之后,我能够成功实现#1而不是#2,这就是我提出这个问题的动机. #2对我而言更具吸引力,因为与模板合并后的加盖实例相比,与模板合并相比,合并模板更容易,而且这种方法似乎是重用dom-repeat之类的嵌套模板的唯一方法.

After much adversity I was able to successfully implement #1 but not #2 and that is motivation for my question. #2 is more appealing to me because it is easier for me to merge templates once rather than merging their resulting stamped instances and this approach seems to be the only way I can reuse nested templates like dom-repeat.

我的主要障碍是,一旦加载了Polymer或可能是它的polyfill,模板就会变得不透明,只能由Polymer模板化功能使用.例如,此代码可以在没有任何Polymer导入的情况下正常运行:

My main obstacle is that once Polymer or perhaps it's polyfill is loaded the templates become opaque and can only be utilized by Polymer templatize functionality. For instance, this code works fine without any Polymer imports:

<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>

在Polymer之外,template.content DOMFragment具有子代,并且设置了innerHTML.但是,一旦使用了Polymer,template.content就不会有子级,而innerHTML为空.这使我无法使用DOM API创建将可用模板混合在一起的新模板,即

Outside of Polymer the template.content DOMFragment has children and innerHTML is set. However once Polymer is used the template.content has no children and the innerHTML is empty. This prevents me from using the DOM API to create a new template that blends the available templates together, i.e.

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组件,而无需重新实现其所有功能.

Perhaps by design importing templates using the standard HTML mechanism didn't work for me. Is there another way to dynamically create/merge templates at runtime with Polymer? Again my main motivation is that I would like to re-use the dom-if and dom-repeat web components nested in a template without reimplementing all of their functionality.

推荐答案

经过进一步的研究,我发现了Polymer 2.0的三个功能,这些功能使我能够得出令人满意的解决方案:

After additional research I discovered three features of Polymer 2.0 that enabled me to produce a satisfactory solution:

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

  1. Whenever Polymer processes DOM templates it memoizes them by default. This template caching prevents expense cloning operations and simplifies template binding. The Polymer 2.0 DOM templating documentation explains that the preserve-content attribute can be added to a template to bypass the optimization allowing the template to be manipulated using native DOM operations.

DOM模板文档还描述了获取自定义元素的原始模板的多种方法.一种选择是调用元素的静态template()方法,另一种选择是使用Polymer.DomModule.import()函数.第二种方法令我感兴趣,因为它允许一个人管理默认模块模板之外的多个模板.

The DOM templating documentation also describes multiple methods of obtaining a custom element's raw template. One option is to call the element's static template() method and another option is to use the Polymer.DomModule.import() function. This second method was of interest to me since it allows one to manage multiple templates beyond the default module template.

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

The Polymer.TemplateStamp API class has an internal _stampTemplate() function that is used to stamp a template into the custom element's DOM. I would have preferred to have used the well documented Polymer.Templatize.templatize() function but it looks for properties and methods on the template itself which in my case was not a custom element with behaviors defined on it.

将这三个功能放在一起,我能够准备一个动态可重用的合并模板,并根据需要使用嵌套的dom-if和dom-repeats.

Putting these three features together I was able to prepare a dynamic reusable merged template utlizing nested dom-ifs and a dom-repeats as I desired.

这是功能结果:

组件:

<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>

这篇关于Polymer 2动态合并模板的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆