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-tabs
和 iron-pages
。 Object.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
元素中触发的事件可以冒泡到应用程序的顶部。
我有一组 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-tabs
和 iron-pages
。 Object.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
元素中触发的事件可以冒泡到应用程序的顶部。