Create controlled elements
In React, form elements are controlled by default, and components need to be used to pass data to form elements to modify the value of form elements. Many benefits:
- Single source of data
- Process user input before rendering
Gem does not handle this behavior by default, you can handle forms like Vanilla JS:
You can use input event to create controlled form elements yourself:
Listen to the
inputevent and store the new value@customElement('form-text') export class FormTextElement extends GemElement { #inputRef = createRef<HTMLInputElement>(); #nextState = ''; #inputHandle = (e: InputEvent) => { this.#nextState = this.#inputRef.value!.value; }; render = () => { return html`<input ${this.#inputRef} @input=${this.#inputHandle} />`; } } Restore the old value and trigger a custom
changeevent@customElement('form-text') export class FormTextElement extends GemElement { #inputRef = createRef<HTMLInputElement>(); @attribute value: string; @emitter change: Emitter<string>; #nextState = ''; #inputHandle = (e: InputEvent) => { this.#nextState = this.#inputRef.value!.value; this.#inputRef.value!.value = this.value; this.change(value); }; render = () => { return html`<input ${this.#inputRef} @input=${this.#inputHandle} />`; } } Modify
<input>element value according to attributes@customElement('form-text') export class FormTextElement extends GemElement { #inputRef = createRef<HTMLInputElement>(); @attribute value: string; @emitter change: Emitter<string>; #nextState = ''; #inputHandle = (e: InputEvent) => { this.#nextState = this.#inputRef.value!.value; this.#inputRef.value!.value = this.value; this.change(value); }; @effect((i) => [i.value]) #updateValue = () => { if (this.value === this.nextState.value) { this.#inputRef.value!.value = this.nextState.value; } else { this.#inputRef.value!.value = this.value; } }; render = () => { return html`<input ${this.#inputRef} @input=${this.#inputHandle} />`; } }
Now <form-text> is a controlled element, it only receives the value of the value attribute:
Finally, you may need to use selectionStart/selectionEnd to handle the pointer position, isComposing Process input method candidates.
Last Updated:
06/09/2025, 03:30:31 PM