Search code examples
javascriptecmascript-6web-componentlit-html

How to render the value in the form using web components or lit-html?


I am using lit-html library to render the data into the form.

The program should auto fill the product name upon inputting the product number. I have written the code but I am unable to auto fill the product name field. For the product name, I have used static dummy data within my JavaScript code.

Is there a way to do this using only web components or using library 'lit-html'?

Below you can find my code

import {html, render} from 'lit-html'

class MyApp extends HTMLElement {
    constructor() {
        super()
        this.attachShadow({mode: 'open'});
        const forms = () => {
            const productNum = new Array();
            const productName = new Array();

            productNum[0] = 123;
            productName[0] = 'Paper';
            productNum [1] = 500;
            productName[1] = 'Laptop';

            function details(products) {
                if (products > 50) {
                    for (let i = 0; i < productNum.length; i++) {
                        if (products == productNum[i]) {
                            this.info.product.value = productName[i]
                        } else {
                            this.info.product.value = ''
                        }
                    }
                }
            }

            return html` <form name = 'info'>
                  <input type="text" name="product" onkeyup="${details(parseInt(this.value, 10))}" maxlength=3>ProductNum
                  <input type="text" name="productName" onkeyup="">ProductName
                </form>`

        }
        const template = html`
            ${forms()}`

        render(template, this.shadowRoot)
    }

}


window.customElements.define('my-app', MyApp)

Solution

  • Objective: (auto)Fill related Form fields with (product) data

    One (native) component way is to let the FORM handle the querying for product data
    Communicate productinfo with Events

    That way there is no dependency between components (other than an EventName),
    and you can have as many related inputs as you need.

    Relevant Custom Element code:

    <my-form>
      <my-input name="productkey"></my-input>
      <my-input name="productname"></my-input>
    </my-form>
    
    customElements.define("my-form", class extends HTMLElement {
      constructor() {
        super();
        let products = productDatabaseMap();
        this.addEventListener("getProduct", evt => {
          let detail={
            product: products[evt.detail.value],
            input: evt.target // input that requested product details
          };
          dispatchEvent(this, "setProduct", detail);
        });
      }
    })
    customElements.define("my-input", class extends HTMLElement {
      constructor() {
        super();
        let input = this.appendChild(document.createElement('input'));
        let name = input.placeholder = this.getAttribute('name');
        this.onkeyup = evt => dispatchEvent(input, "getProduct", evt.target);
        this.closest("my-form").addEventListener("setProduct", evt => {
          let product = evt.detail.product;
          if (product instanceof Product) input.value = product[name];
          else if (evt.detail.input !== input) input.value = '';
        });
    
      }
    })
    
    • Data is stored in a Map with both productkey AND productname as lookup keys
    • each input keypress sends a "getProduct" Event UP the DOM tree
    • An Event Listener on my-form gets the product from the lookup Map
    • The form dispatches a "setProduct" Event with the Product or undefined product
    • ALL inputs listen for the "setProduct" Event
    • if a product was returned ALL input fields set the corresponding value

    Working JSFiddle: https://jsfiddle.net/WebComponents/L2dcruoh/

    If you wrap <my-input> in a shadowRoot you need more boilerplate code and be aware event.detail then needs composed:true to make it cross shadow boundaries. And event.target will reference the (last) component it came from... not the input field that triggered that event