Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom litelement select not rerendering correctly

I've created a custom select-component with LitElement:

import { LitElement, html } from 'lit-element';

class CustomSelect extends LitElement {
    static get properties()  {
        return {
            options: { type: Array },
            selected: { type: String },
            onChange: { type: Function }
        };
    }
    constructor() {
        super();
        this.options = [];
    }
    render() {
        return html`
            <select @change="${this.onChange}">
                ${this.options.map(option => html`
                    <option value="${option.value}" ?selected=${this.selected === option.value}>${option.text}</option>
                `)}
            </select>
        `;
    }
    createRenderRoot() {
        return this;
    }
}

customElements.define('custom-select', CustomSelect);

I pass in options, selected and onChange as properties when I create the element. On the first render, everything works fine. All options are rendered and the selected value is reflected in the select. However, if I change selected it doesn't seem to update the selected option. If I inspect the element with dev-tools, the selected attribute is set correctly, but if I start querying the element for its value, it returns the wrong value.

One thing I tried is to add an id attribute to the element via dev-tools after the select has been rendered. If I then change the selected property on CustomSelect, the id attribute persists in the DOM, which says to me that the select is not re-rendered, which is what causing the issue, and why it's working on the first render.

I've tried setting the value and selectedIndex properties on the select-element, but it doesn't seem to affect anything in a meaningful way.

I've logged everywhere (beginning in render() and in the options-map) and all input values are correct.

like image 587
CaptainBarefoot Avatar asked Apr 25 '19 21:04

CaptainBarefoot


2 Answers

It's I think, rendering time and selected property definition on onChange functions timing conflict. So, better to assign a setTimeout in onChange then it's working properly. At my example at below link. I faced the same when I remove setTimeout Also, you don't need to declare onChange as function at properties.

Demo

static get properties()  {
   return {
        options: { type: Array },
        selected: { type: String }
    };
}
constructor() {
    super();
    this.options = [{value:1, text:"ben"},{value:2, text:"sen"},{value:3, text:"oo"},{value:4, text:"biz"},{value:5, text:"siz"},{value:6, text:"onlar"}];
    this.selected = 3
}
render() {
    return html`

        <select id="sel" @change="${this.onChange}">
            ${this.options.map(option => html`
                <option value="${option.value}" ?selected=${this.selected === option.value}>${option.text}</option>
            `)}
        </select>
        <button @click="${this.changeSelected}">Random chage selected option</button>
    `;
}

onChange(x){
 setTimeout(()=>{ 
    this.selected = this.shadowRoot.querySelector('#sel').value
    console.log('Selected -->', this.selected );
   },300)
}
changeSelected(){
  this.selected = (this.options[Math.floor(Math.random() * 6)].value)
  console.log(this.selected)
} 
like image 54
HakanC Avatar answered Nov 16 '22 06:11

HakanC


Working example:

https://stackblitz.com/edit/litelement-testt-csgkmc

Fixed using the repeat directive.

import { repeat } from 'lit-html/directives/repeat';

${repeat(this.sortedRows, e => e, row => html`
    <tr>
    ${row.map((cell, index) => html`
      <td>${this.onRenderCell(cell, index, row)}</td>
    `)}
    </tr>
`)}

https://github.com/Polymer/lit-element/issues/805#issuecomment-530461555

like image 30
Andrew Noblet Avatar answered Nov 16 '22 07:11

Andrew Noblet