Stenciljs:前置/附加插槽元素的正确方法是什么?

发布于 2025-01-10 10:23:56 字数 1267 浏览 1 评论 0原文

我在使用 Web 组件中的生命周期方法时遇到一些问题。

我们想要动态地排序作为槽传入的子元素。

为了说明这一点,此 Web 组件采用一个 prop,iconPos,并将确定图标是否放置在插槽的开头或结尾。

<my-component iconPos="start"> 
   <img src="/path/icon.svg" /> 
   <div>{this.list}</div> 
</my-component>

我没有任何运气让它与 ref 一起工作:

dc6b89e7.js:2926 TypeError: Cannot read properties of undefined (reading 'prepend')

这就是我所拥有的far:

 @State() slotElement!: HTMLDivElement;
 @Prop() iconPos: 'start' | 'end';
...

  private createSlots() {
    switch (this.iconPos) {
      case 'start':
        this.slotElement.prepend(<img />);
        break;
      case 'end':
        this.slotElement.append(<img />);
        break;
      default:
        throw new Error(
          `Invalid value \`${this.iconPos}\`, passed into \`iconPos\`. Expected valid values are \`start\`, \`end\``.
        );
    }
  }

  render() {
    return (
    // iconPos="start"
      <parent-component>
        <div ref={(el) => (this.slotElement= el as HTMLDivElement)}>
          <slot></slot>
        </div>
      </parent-component>
    )
  }

如果可能的话,我宁愿不使用 CSS 解决方案。任何帮助将不胜感激!

I am having some trouble with the lifecycle methods in web components.

We want to dynamically order child elements being passed in as slots.

To illustrate, this web component takes a prop, iconPos, and will determine whether the icon will be placed at the start or end of the slot.

<my-component iconPos="start"> 
   <img src="/path/icon.svg" /> 
   <div>{this.list}</div> 
</my-component>

I haven't had any luck getting it working with ref:

dc6b89e7.js:2926 TypeError: Cannot read properties of undefined (reading 'prepend')

Here's what I have so far:

 @State() slotElement!: HTMLDivElement;
 @Prop() iconPos: 'start' | 'end';
...

  private createSlots() {
    switch (this.iconPos) {
      case 'start':
        this.slotElement.prepend(<img />);
        break;
      case 'end':
        this.slotElement.append(<img />);
        break;
      default:
        throw new Error(
          `Invalid value \`${this.iconPos}\`, passed into \`iconPos\`. Expected valid values are \`start\`, \`end\``.
        );
    }
  }

  render() {
    return (
    // iconPos="start"
      <parent-component>
        <div ref={(el) => (this.slotElement= el as HTMLDivElement)}>
          <slot></slot>
        </div>
      </parent-component>
    )
  }

I would prefer to not use a CSS solution if possible. Any help would be much appreciated!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

寂寞陪衬 2025-01-17 10:23:56

开槽内容未移动元素;它反映了!!

因此,所有样式和元素操作都必须在“lightDOM”中完成

对于(非常)长的阅读,请参阅:
::用于shadowDOM中嵌套子项的slotted CSS选择器slot

这意味着你必须在ligtDOM中附加你的元素:

this.append(this.firstElementChild)

您无法在解析innerHTML之前读取它;所以你需要等待innerHTML 元素被创建。这样你就会看到 DOM 的变化。
更好的方法可能是不使用 并将图标和内容声明为属性,并让 Web 组件创建 HTML 。

<style>
  span::after { content: attr(id) }
  #FOO { background: lightgreen }
</style>

<my-component>
  <span id="FOO"></span>
  <span id="BAR"></span>
</my-component>
<my-component reversed>
  <span id="FOO"></span>
  <span id="BAR"></span>
</my-component>

<script>
  window.customElements.define('my-component', class extends HTMLElement {
    constructor() {
      super().attachShadow({mode:'open'})
             .innerHTML = `<style>::slotted(span){background:gold}</style>
                           ${this.nodeName}<slot></slot><br>`;
    }
    connectedCallback() {
      setTimeout(() => { // make sure innerHTML is parsed!
        if (this.hasAttribute("reversed")) {
          this.append(this.firstElementChild);
        }
      })
    }
  });
</script>

Slotted content is NOT MOVED to <slot> elements; it is reflected!!

So all styling and element operations must be done in "lightDOM"

For (very) long read see:
::slotted CSS selector for nested children in shadowDOM slot

That means you have to append your elements in ligtDOM with:

this.append(this.firstElementChild)

You can't read the <my-component> innerHTML before it is parsed; so you need to wait till the innerHTML elements are created. Thus you will see the DOM change.
A better method might be to not use <slot> and declare your icon and content as attributes, and have the Web Component create the HTML.

<style>
  span::after { content: attr(id) }
  #FOO { background: lightgreen }
</style>

<my-component>
  <span id="FOO"></span>
  <span id="BAR"></span>
</my-component>
<my-component reversed>
  <span id="FOO"></span>
  <span id="BAR"></span>
</my-component>

<script>
  window.customElements.define('my-component', class extends HTMLElement {
    constructor() {
      super().attachShadow({mode:'open'})
             .innerHTML = `<style>::slotted(span){background:gold}</style>
                           ${this.nodeName}<slot></slot><br>`;
    }
    connectedCallback() {
      setTimeout(() => { // make sure innerHTML is parsed!
        if (this.hasAttribute("reversed")) {
          this.append(this.firstElementChild);
        }
      })
    }
  });
</script>

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文