0

I have a problem with two web components created using lit-element v3.2.0 and I'm using custom events to emit the input value from the child component up to the parent component.

The form-input component is a reusable input that extracts the input value and emits it to the parent component with a custom event named "new-value" which is dispatched when the user writes in the field.

The form-container component contains the form-input, in here I'm binding the custom event "new-value" to a method called "updateInputValue" which should reassign the inputValue property with the emitted value from the child component, but instead is stuck with whatever value initialized in the parent constructor.

form-container.js

static get properties() {
    return {
        inputValue: { type: String },
        items: { type: Array },
    }
}

constructor() {
    super()
    this.inputValue = ''
    this.items = []
}

render() {
    return html`<form>
        <h1>My form container</h1>
        <form-input
            @new-value=${this.updateInputValue}
            fieldName="name"
            id="name"
            label="Name"
            placeholder="Enter anything"
            value="${this.inputValue}"
        ></form-input>
        <button @click=${this.addNewItem} type="submit">Add</button>
        <form-list .items="${this.items}"></form-list>
    </form>`
}

updateInputValue(e) {
    // Update input value with the value emitted from the form-input
    this.inputValue = e.detail
}

addNewItem(e) {
    // Add new item to the list
    e.preventDefault()
    console.log('add new item with the following value:', this.inputValue)

form-input.js

static get properties() {
    return {
        value: { type: String },
        fieldName: { type: String },
        label: { type: String },
        placeholder: { type: String },
        type: { type: String },
    }
}

constructor() {
    super()
}

render() {
    return html`
        <div>
            <label for="name">${this.label}</label>
            <input
                @input=${this.dispatchEvent}
                id="${this.fieldName}"
                name="${this.fieldName}"
                placeholder="${this.placeholder}"
                type="${this.type || 'text'}"
                value="${this.value}"
            />
        </div>
    `
}

dispatchEvent(e) {
    // Emit the new value from the input to the parent component
    const target = e.target
    if (target) {
        this.dispatchEvent(
            new CustomEvent('new-value', {
                detail: target.value,
            })
        )
    }
}

Any help will be very much appreciated.

1 Answers1

3

You are overwriting the dispatchEvent method and calling yourself. Rename the dispatchEvent Method and give it a meaningful name. It works perfectly.

<script type="module">
import {
  LitElement,
  html, css
} from "https://unpkg.com/lit-element/lit-element.js?module";

class FormInput extends LitElement {
  static get properties() {
   return {
        value: { type: String },
        fieldName: { type: String },
        label: { type: String },
        placeholder: { type: String },
        type: { type: String },
    };
  }
 
  render() {
    return html`
        <div>
            <label for="name">${this.label}</label>
            <input
                @input=${this.changedInput}
                id="${this.fieldName}"
                name="${this.fieldName}"
                placeholder="${this.placeholder}"
                type="${this.type || 'text'}"
                value="${this.value}"
            />
        </div>
    `
  } 

  changedInput(e) {
    // Emit the new value from the input to the parent component
    
        console.log(e.target.value);
        
        const myevent = new CustomEvent('my-event', {
                bubbles: true,
                composed: true,
                detail: {
                  value: e.target.value
                }
            }) 
        this.dispatchEvent(
            myevent
        );
  }
}

class FormContainer extends LitElement {
  
  static get properties() {
    return {
      name: {
        inputValue: { type: String },
      }      
    };
  }
 
  updateInputValue(e) {
      console.log('received ' + e.detail.value);
      this.inputValue = e.detail.value;
  }
  
  addNewItem(e) {
    // Add new item to the list
    e.preventDefault()
    console.log('add new item with the following value:', this.inputValue);
  }
 
  render() {
    return html`<form>
        <h1>My form container</h1>
        <form-input
            @my-event="${this.updateInputValue}"
            fieldName="name"
            id="name"
            label="Name"
            placeholder="Enter anything"
            value="${this.inputValue}"
        ></form-input>
        <button @click=${this.addNewItem} type="submit">Add</button>
        <form-list .items="${this.items}"></form-list>
    </form>
    `;
  }
}

customElements.define("form-container", FormContainer);
customElements.define("form-input", FormInput);
</script>
<form-container></form-container>
Christian
  • 3,503
  • 1
  • 26
  • 47