What is best way to create custom form element of which behave default input element #3201
Replies: 1 comment 1 reply
-
I ended up with something like this, but I have no idea whether it's correct. This is a work in progress and I wouldn't recommend using it, there are probably various problems around inconsistent attribute reflection, poor handling of focus/blur events etc. I think the key is to use the The component with the @query('input')
inputElement!: HTMLInputElement;
@property()
name: string = '';
@property()
type: HTMLInputElementType = 'text';
@property()
label: string = '';
@property({ reflect: true })
value: string | number = '';
@property()
min: string | undefined = undefined;
@property()
max: string | undefined = undefined;
@property()
placeholder: string | undefined = undefined;
@property()
tooltip: string | undefined = undefined;
@property({ type: Boolean })
readonly: boolean = false;
@property({ type: Boolean })
required: boolean = false;
@property({ type: Boolean, reflect: true})
focused: boolean = false;
@state()
hasValue: boolean = false;
static formAssociated = true;
private _internals: ElementInternals;
constructor() {
super();
this._internals = this.attachInternals();
}
protected willUpdate(_changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>): void {
if (this.value !== '' && this.value !== null && this.value !== undefined) {
this.hasValue = true;
this._internals.setFormValue(this.inputElement?.value);
}
}
private _input() {
this.value = this.inputElement.value;
}
private _focus() {
this.focused = true;
if (this.hasValue !== true) {
this.hasValue = true;
};
}
private _blur() {
this.focused = false;
if (this.hasValue !== false && !this.value) {
this.hasValue = false;
};
} And here's some very horrible code that I'm using to try and observe value changes at the import { ElementPart, noChange, Part, ReactiveController, ReactiveControllerHost } from 'lit';
import { Directive, directive, DirectiveParameters } from 'lit/directive.js';
export class FormDirective extends Directive {
update(_part: Part, [controller]: DirectiveParameters<this>): unknown {
if (controller._formElement == undefined) {
const formElement = (_part as ElementPart).element;
controller.setFormElement(formElement as HTMLFormElement);
}
return noChange;
}
// @ts-ignore
render(controller: IFormController): unknown {
return noChange;
}
}
const formDirective = directive(FormDirective);
export interface IFormController extends ReactiveController {
setFormElement(formElement: HTMLFormElement): void;
_formElement: HTMLFormElement | undefined;
}
export class FormController<T> implements IFormController {
host: ReactiveControllerHost;
form: T;
_formElement: HTMLFormElement | undefined;
constructor(host: ReactiveControllerHost, form: T) {
(this.host = host).addController(this);
this.form = form;
}
setFormElement(formElement: HTMLFormElement) {
this._formElement = formElement;
this._formElement.addEventListener('input', (ev: Event) => this.handleInput(ev as InputEvent));
this.loadFormValues();
}
private handleInput(event: InputEvent): void {
event.stopPropagation();
const target = event.target as HTMLInputElement;
this.setFormValue(target.name, target.value);
}
loadFormValues() {
Object.entries(this.form).forEach(([k, v]) => {
const selector = `[name="${k}"]`;
const el = this._formElement?.querySelector(selector) as HTMLInputElement | undefined;
if (el) {
el.value = v;
}
})
}
setFormValue(formControlName: string, value: any) {
const fcName = formControlName as keyof T; // TODO: eurgh
this.form[fcName] = value;
this.host.requestUpdate();
}
observe() {
return formDirective(this);
}
hostConnected() {
}
hostDisconnected() {
this.form = undefined as any;
this._formElement?.removeEventListener('input', (ev: Event) => this.handleInput(ev as InputEvent));
}
} |
Beta Was this translation helpful? Give feedback.
-
I want to create custom input form element using lit dev, which should behave like default input element, user can should get value of element as we are getting as normal input element. I found couple of answers in stackover flow but still finding any better way for doing it.
Stackoverflow answers:
Does there is any way so that its behave same like input element? Like in Angular we are using
formControlName
to bind input How can we do that things?Beta Was this translation helpful? Give feedback.
All reactions