我目前正在尝试使用StencilJS创建一些Web组件。
现在我知道这里有<slot />
个名为插槽的东西。来自React,我想插槽与React中的子代相似。您可以在React中使用子级来做很多事情。我经常做的事情:
您将如何使用广告位/网络组件/ stencilJS呢?
我可以使用
在Stencil中获取Web组件的主机元素。@Element() hostElement: HTMLElement;
我使用类似
的组件<my-custom-component>
<button>1</button>
<button>2</button>
<button>3</button>
</my-custom-component>
我想渲染类似的东西
render() {
return slottedChildren ?
<span>No Elements</span> :
<ul class="my-custom-component">
slottedChildren.map(child => <li class="my-custom-element>{child}</li>)
</ul>;
}
亲切的问候
答案 0 :(得分:7)
使用插槽,您无需在渲染函数中添加条件。您可以将no children元素(在您的示例中为span)放置在slot元素内,如果未向该slot提供任何子元素,它将退回到它的位置。 例如:
render() {
return (
<div>
<slot><span>no elements</span></slot>
</div>
);
}
回答您所写的评论-您可以执行此操作,但需要一些编码,而不是立即可用。每个插槽元素都有一个assignedNodes
函数。利用这些知识和对Stencil组件生命周期的理解,您可以执行以下操作:
import {Component, Element, State} from '@stencil/core';
@Component({
tag: 'slotted-element',
styleUrl: 'slotted-element.css',
shadow: true
})
export class SlottedElement {
@Element() host: HTMLDivElement;
@State() children: Array<any> = [];
componentWillLoad() {
let slotted = this.host.shadowRoot.querySelector('slot') as HTMLSlotElement;
this.children = slotted.assignedNodes().filter((node) => { return node.nodeName !== '#text'; });
}
render() {
return (
<div>
<slot />
<ul>
{this.children.map(child => { return <li innerHTML={child.outerHTML}></li>; })}
</ul>
</div>
);
}
}
这不是最佳解决方案,它将要求插槽的样式应将显示设置为none(因为您不想显示它)。 此外,它仅适用于仅需要呈现且不需要事件或其他任何东西的简单元素(因为它仅将它们用作html字符串而不用作对象)。
答案 1 :(得分:0)
谢谢你吉尔的答案。
我之前在想类似的事情(设置状态等-由于可能会出现计时问题)。不过,我不喜欢该解决方案,因为您要在componentDidLoad内进行状态更改,这会在组件加载后立即触发另一个加载。这似乎很脏而且不适合做。
innerHTML={child.outerHTML}
的一点帮助对我很有帮助。
似乎您也可以轻松做到:
import {Component, Element, State} from '@stencil/core';
@Component({
tag: 'slotted-element',
styleUrl: 'slotted-element.css',
shadow: true
})
export class SlottedElement {
@Element() host: HTMLDivElement;
render() {
return (
<div>
<ul>
{Array.from(this.host.children)
.map(child => <li innerHTML={child.outerHTML} />)}
</ul>
</div>
);
}
}
我认为您可能会遇到计时问题,因为在render()
期间,主机的子元素已被删除,以为render()
返回的内容腾出空间。但是由于阴影域和光域在主机组件中可以很好地共存,所以我认为应该没有任何问题。
我真的不知道为什么您必须使用innerHTML
。来自React,我很习惯:
{Array.from(this.host.children)
.map(child => <li>{child}</li>)}
我认为这是基本的JSX语法,并且由于Stencil也使用JSX,所以我也可以这样做。虽然不起作用。 innerHTML
帮了我大忙。再次感谢。
编辑:但是,如果您不使用shadow-dom,则会出现我提到的计时问题。一些奇怪的事情开始发生,最终您将有很多重复的孩子。 虽然可以做到(可能会有副作用):
import {Component, Element, State} from '@stencil/core';
@Component({
tag: 'slotted-element',
styleUrl: 'slotted-element.css',
shadow: true
})
export class SlottedElement {
children: Element[];
@Element() host: HTMLDivElement;
componentWillLoad() {
this.children = Array.from(this.host.children);
this.host.innerHTML = '';
}
render() {
return (
<div>
<ul>
{this.children.map(child => <li innerHTML={child.outerHTML} />)}
</ul>
</div>
);
}
}