aguileraq
aguileraq

Reputation: 21

How can get custom tags inside web component

I'm new in webcomponents with stenciljs, I'm testing creating a select, the idea with this code create and render the select:

<rhx-select label-text="A select web component">
      <rhx-select-item value="1" text="option 1"/>
      <rhx-select-item value="2" text="option 2"/>
</rhx-select>

The problem i have is how can i get the tags that inside my web component?

this is my code:

import { Component, h, Prop, } from '@stencil/core';

@Component({
  tag: 'rhx-select',
  styleUrl: 'select.css',
  shadow: true,
})

export class RhxSelect {
    @Prop() labelText: string = 'select-rhx';

    @Prop() id: string;
    
    @Element() el: HTMLElement;

    renderOptions() {
        let data = Array.from(this.el.querySelectorAll('rhx-select-item'));
        return data.map((e) =>{
            <option value={e.attributes.getNamedItem('value').value}>{e.attributes.getNamedItem('text').value}</option>
        });
    }

    render(){
        return (
            <div>
                
                <label htmlFor={this.id}>
                    {this.labelText}
                </label>

                <select id={this.id} class="rhx-select">
                  {this.renderOptions()}
                </select>

            </div>
        )
    }   
}

Thank you for your time.

Upvotes: 0

Views: 699

Answers (2)

Simon H&#228;nisch
Simon H&#228;nisch

Reputation: 4978

this.el.querySelectorAll won't return any elements until after the component has rendered once, so that its children are available in the DOM. Therefore you will have to use something like the componentDidLoad hook:

export class RhxSelect {
  // ...

  @State()
  items: HTMLRhxSelectItemElement[] = [];

  componentDidLoad() {
    this.items = Array.from(this.el.querySelectorAll('rhx-select-item'));
  }

  render() {
    return (
      <div>
        <label htmlFor={this.id}>
            {this.labelText}
        </label>

        <select id={this.id} class="rhx-select">
          {this.items.map(item => (
            <option value={item.getAttribute('value')}>{item.getAttribute('text')}</option>
          ))}
        </select>
      </div>
    )
  }
}

Note however that componentDidLoad is only executed once, after the component has loaded. If you want your component to support dynamic changes to the options, then you'll have to use something else, like componentDidRender, but then you'll also have to make sure you don't end up with an infinite render loop. There's also a couple ways to solve this, by combining different lifecycle methods.

See https://stenciljs.com/docs/component-lifecycle for a list of all available lifecycle methods.

Upvotes: 0

Thomas
Thomas

Reputation: 8859

If you add the @Element() decorator you can parse the children with vanilla JS:

getItems() {
  return Array.from(this.el.querySelectorAll('rhx-select-item'));
}

You can then use those elements and their properties/attributes however you want, for example to generate a list of <option> elements.

A good example is ion-select which gets the children in the childOpts() getter function.

A couple things to keep in mind:

  1. You'll probably want to hide the items with display: none
  2. If the options might change after the initial load you'll need to listen for those changes. Ionic uses the watchForOptions function.

Upvotes: 0

Related Questions