Subclass 聚合物元素填充基础 class 插入点

Subclass Polymer element filling base class insertion points

我有一组 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 在 media-tab 子类中是 相同的 ,我想找到一种方法将该逻辑移动到超类中,而无需代码在超类中需要了解子类的实现细节。

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

我已经想出了一个使用 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

我以前的解决方案在选项卡内容中出现冒泡事件问题,所以我不得不想出另一个解决方案:

<!-- 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> 插入点添加到 iron-pages。

// 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);
  });
}

现在我只将 children 附加到 paper-tabs 元素,而不是 paper-tabsiron-pagesObject.getPrototypeOf(child.constructor) returns child 的 superclass 的构造函数;这意味着我不是简单地假设 child 的 superclass 是 MediaTab,尽管这可以改进,因为我 am 假设 child的 superclass 有一个带有 id="title" 和 属性 tabTitle.

的元素
<!-- media-tab.html -->
<paper-tab id="title"></paper-tab>
// media-tab.js
static get properties() {
  return {
    tabTitle: String,
  };
}

media-tab模板不再包含内容div,superclass定义了tabTitle 属性中引用的内容media-tabs.js.

<!-- media-blackout.html -->
<p>Lorem ipsum dolor sit amet</p>
<!-- repeat above for media-playlists, media-queue, and media-slates -->

media-tab subclasses 不再将其标签标题存储在 HTML 中,它们只有内容。

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

子 class 的标题不是 span,而是 class 的 属性。

此解决方案不使用混合,从 MediaTab 元素中触发的事件可以冒泡到应用程序的顶部。