子类Polymer元素填充基类插入点

时间:2017-11-10 19:44:37

标签: javascript html polymer-2.x

我有一组Polymer组件,它们是另一个组件的子类。我想基于子类的模板填充超类的模板。 (对于寻找类似问题解决方案的其他人,请参阅here,遗憾的是没有解决方案。)用法如下所示:

<!-- control.html -->
<media-tabs tabs-title="Alternative input">
  <template>
    <media-blackout></media-blackout>
    <media-slates></media-slates>
    <media-playlists></media-playlists>
    <media-queue></media-queue>
  </template>
</media-tabs>
<!-- media-tabs.html -->
<paper-card heading="[[tabsTitle]]" class="fill-h fill-v">
  <div class="card-content">
    <paper-tabs id="tabs" selected="{{selectedIndex}}" selectable>
    </paper-tabs>
    <iron-pages id="content" selected="{{selectedIndex}}">
    </iron-pages>
  </div>
</paper-card>
// media-tabs.js
ready() {
  super.ready();
  this.selectedIndex = 0;
  const template = this.querySelector('template');
  const instance = this._stampTemplate(template);
  const tabs = Array.from(instance.children);
  tabs.forEach((elm) => {
    const content = elm.constructor.template.content;
    this.$.tabs.appendChild(content.querySelector('#title'));
    this.$.content.appendChild(content.querySelector('#content'));
  });
}
<!-- media-tab.html -->
<paper-tab id="title"></paper-tab>
<div id="content"></div>
<!-- media-blackout.html -->
<span id="title">Blackout</span>
<div id="content">
  <p>Lorem ipsum dolor sit amet</p>
</div>
<!-- repeat above for media-playlists, media-queue, and media-slates -->
// media-blackout.js -- is a subclass of media-tab
static get template() {
  if (!superTemplate) {
    const thisTemplate = Polymer.DomModule.import(this.is, 'template');
    superTemplate = MediaTab.template.cloneNode(true);
    const title = thisTemplate.content.querySelector('#title').textContent;
    const content = thisTemplate.content.querySelector('#content');
    const children = Array.from(content.children);

    superTemplate.content.querySelector('#title').innerHTML = title;
    children.forEach((child) => {
      superTemplate.content.querySelector('#content').appendChild(child);
    });
  }
  return superTemplate;
}
// repeat above for media-playlists, media-queue, and media-slates

此设置有效。但是,媒体选项卡子类中的template() getter 相同,我想找到一种方法将该逻辑移动到超类中,而不需要知道超类中的代码子类实现细节。

我试图为超类需要提取的两个字段(例如const title = this.templateTitleElement.textContent;)创建getter,但是我的实现要么以某种方式戳元素的template属性(导致堆栈溢出),或者尝试在元素可用之前访问(例如,return this.$.title;)。

2 个答案:

答案 0 :(得分:0)

我设法使用mixins提出解决方案;它不是传统OOP的继承,但它的功能却没有复制相同的功能四次。

// title-content-mixin.js
const titleContentMixin = (function() {
  const titleContentMixin = (superClass, options = {}) => {
    const titleQuery = options.titleQuery || '#title';
    const contentQuery = options.contentQuery || '#content';
    const superTitleQuery = options.superTitleQuery || titleQuery;
    const superContentQuery = options.superContentQuery || contentQuery;

    return class TitleContentMixin extends superClass {
      static get template() {
        if (this.is === superClass.is || superClass.is.length === 0) return;

        const thisTemplate = Polymer.DomModule.import(this.is, 'template')
            .cloneNode(true);
        const superTemplate = superClass.template.cloneNode(true);
        const title = thisTemplate.content.querySelector(titleQuery).textContent;
        const content = thisTemplate.content.querySelector(contentQuery);
        const children = Array.from(content.children);

        superTemplate.content.querySelector(superTitleQuery).innerHTML = title;
        children.forEach((child) => {
          superTemplate.content.querySelector(superContentQuery)
              .appendChild(child);
        });
        return superTemplate;
      }
    };
  };

  return Polymer.dedupingMixin(titleContentMixin);
})();
// media-blackout.js
class MediaBlackout extends titleContentMixin(MediaTab) {
  // ...
}
// repeat above for media-playlists, media-queue, and media-slates

答案 1 :(得分:0)

我以前的解决方案存在从标签内容中冒泡事件的问题,因此我不得不提出另一种解决方案:

<!-- control.html -->
<media-tabs tabs-title="Alternative input" on-play-slate="handlePlayTapped">
  <media-blackout></media-blackout>
  <media-slates></media-slates>
  <media-playlists></media-playlists>
  <media-queue></media-queue>
</media-tabs>

您可以看到我已从media-tabs声明中删除了该模板。

<!-- media-tabs.html -->
<paper-card heading="[[tabsTitle]]">
  <div class="card-content">
    <paper-tabs id="tabs" selected="{{selectedIndex}}" selectable></paper-tabs>
    <iron-pages id="content" selected="[[selectedIndex]]">
      <slot></slot>
    </iron-pages>
  </div>
</paper-card>

您可以看到我已将<slot>插入点添加到铁页。

// media-tabs.js
ready() {
  super.ready();
  const children = Array.from(this.children);
  children.forEach((child) => {
    const template = Object.getPrototypeOf(child.constructor).template;
    const instance = this._stampTemplate(template);
    instance.$.title.innerHTML = child.tabTitle;
    this.$.tabs.appendChild(instance.$.title);
  });
}

现在我只将儿童附加到paper-tabs元素,而不是paper-tabsiron-pagesObject.getPrototypeOf(child.constructor)返回child超类的构造函数;这意味着我不仅仅假设孩子的超类是MediaTab,虽然这可以改进,因为我假设孩子的超类有一个{{1的元素}和一个属性id="title"

tabTitle
<!-- media-tab.html -->
<paper-tab id="title"></paper-tab>

// media-tab.js static get properties() { return { tabTitle: String, }; } 模板不再包含内容div,超类定义media-tab中引用的tabTitle属性。

media-tabs.js

<!-- media-blackout.html --> <p>Lorem ipsum dolor sit amet</p> <!-- repeat above for media-playlists, media-queue, and media-slates --> 子类不再将其标签标题存储在HTML中,它们只包含内容。

media-tab

不是在// media-blackout.js class MediaBlackout extends MediaTab { // ... static get properties() { return { tabTitle: { type: String, value: 'Blackout', readOnly: true, }, }; } } // repeat above for media-playlists, media-queue, and media-slates 中使用子类的标题,而是将其定义为类的属性。

此解决方案不使用mixin,从span元素中触发的事件可能会冒泡到应用程序的顶部。