Skip to content
Tech News
← Back to articles

Show HN: GolemUI – The new paradigm for JavaScript forms

read original more articles
Why This Matters

GolemUI introduces a new paradigm for building dynamic, user-friendly JavaScript forms with react-hook-form, streamlining form management and validation. Its flexible approach allows developers to create adaptable forms that respond to user input, enhancing user experience and reducing development complexity. This innovation is significant for the tech industry as it promotes more efficient, maintainable, and scalable form solutions for web applications.

Key Takeaways

import { useForm, useWatch } from 'react-hook-form' ; type FormData = { email : string ; password : string ; accountType : 'Free' | 'Pro' | 'Enterprise' ; companyName ?: string ; seats ?: number ; subscribe : boolean ; frequency ?: string ; terms : boolean ; }; export function SignupForm () { const { register , control , handleSubmit , formState : { errors } } = useForm < FormData >({ defaultValues: { accountType: 'Free' , subscribe: false , terms: false }, }); const accountType = useWatch ({ control, name: 'accountType' }); const subscribe = useWatch ({ control, name: 'subscribe' }); return ( < form onSubmit = { handleSubmit (( data ) => console. log (data))}> < label >Email < input type = "email" { ... register ( 'email' , { required: true , pattern: / ^ [ ^ \s@] + @ [ ^ \s@] + \. [ ^ \s@] +$ / , })} /> {errors.email && < span >Valid email required</ span >} </ label > < label >Password < input type = "password" { ... register ( 'password' , { required: true , minLength: 8 , })} /> {errors.password && < span >Min 8 characters</ span >} </ label > < label >Account type < select { ... register ( 'accountType' , { required: true })}> < option >Free</ option > < option >Pro</ option > < option >Enterprise</ option > </ select > </ label > {accountType !== 'Free' && ( < label >Company name < input { ... register ( 'companyName' , { required: true })} /> {errors.companyName && < span >Required</ span >} </ label > )} {accountType === 'Enterprise' && ( < label >Seats < input type = "number" { ... register ( 'seats' , { required: true , min: 5 , max: 1000 , valueAsNumber: true , })} /> {errors.seats && < span >Between 5 and 1000</ span >} </ label > )} < label > < input type = "checkbox" { ... register ( 'subscribe' )} /> Subscribe to product updates </ label > {subscribe && ( < label >Frequency < select { ... register ( 'frequency' , { required: true })}> < option >Daily</ option > < option >Weekly</ option > < option >Monthly</ option > </ select > </ label > )} < label > < input type = "checkbox" { ... register ( 'terms' , { validate : ( v ) => v === true || 'Required' , })} /> I accept the terms of service </ label > < button type = "submit" >Sign up</ button > </ form > ); }

import { Component, signal } from '@angular/core' ; import { form, Control, schema, required, email, minLength, validate, } from '@angular/forms/signals' ; interface Signup { email : string ; password : string ; accountType : 'Free' | 'Pro' | 'Enterprise' ; companyName : string ; seats : number | null ; subscribe : boolean ; frequency : 'Daily' | 'Weekly' | 'Monthly' ; terms : boolean ; } const signupSchema = schema < Signup >(( data ) => { required (data.email); email (data.email); required (data.password); minLength (data.password, 8 ); validate (data.companyName, ({ value , valueOf }) => valueOf (data.accountType) !== 'Free' && ! value () ? { kind: 'required' } : null ); validate (data.seats, ({ value , valueOf }) => { if ( valueOf (data.accountType) !== 'Enterprise' ) return null ; const v = value (); return v != null && v >= 5 && v <= 1000 ? null : { kind: 'seats' }; }); validate (data.terms, ({ value }) => value () ? null : { kind: 'mustAccept' }); }); @ Component ({ selector: 'app-signup' , imports: [Control], template: ` <form (submit)="onSubmit($event)"> <label>Email <input [control]="f.email" type="email" /> </label> <label>Password <input [control]="f.password" type="password" /> </label> <label>Account type <select [control]="f.accountType"> <option>Free</option> <option>Pro</option> <option>Enterprise</option> </select> </label> @if (data().accountType !== 'Free') { <label>Company name <input [control]="f.companyName" /> </label> } @if (data().accountType === 'Enterprise') { <label>Seats <input [control]="f.seats" type="number" min="5" max="1000" /> </label> } <label> <input [control]="f.subscribe" type="checkbox" /> Subscribe to product updates </label> @if (data().subscribe) { <label>Frequency <select [control]="f.frequency"> <option>Daily</option> <option>Weekly</option> <option>Monthly</option> </select> </label> } <label> <input [control]="f.terms" type="checkbox" /> I accept the terms of service </label> <button type="submit" [disabled]="!f().valid()">Sign up</button> </form> ` , }) export class SignupForm { data = signal < Signup >({ email: '' , password: '' , accountType: 'Free' , companyName: '' , seats: null , subscribe: false , frequency: 'Daily' , terms: false , }); f = form ( this .data, signupSchema); onSubmit ( e : Event ) { e. preventDefault (); if ( this . f (). valid ()) console. log ( this . data ()); } }

import { LitElement, html } from 'lit' ; import { customElement, state } from 'lit/decorators.js' ; type Account = 'Free' | 'Pro' | 'Enterprise' ; @ customElement ( 'signup-form' ) export class SignupForm extends LitElement { @ state () email = '' ; @ state () password = '' ; @ state () accountType : Account = 'Free' ; @ state () companyName = '' ; @ state () seats : number | null = null ; @ state () subscribe = false ; @ state () frequency = 'Daily' ; @ state () terms = false ; @ state () errors : Record < string , string > = {}; private bind = ( key : keyof this ) => ( e : Event ) => { const t = e.target as HTMLInputElement ; ( this as any )[key] = t.type === 'checkbox' ? t.checked : t.value; }; private validate () { const e : Record < string , string > = {}; if ( ! / ^ [ ^ \s@] + @ [ ^ \s@] + \. [ ^ \s@] +$ / . test ( this .email)) e.email = 'Valid email' ; if ( this .password. length < 8 ) e.password = 'Min 8 characters' ; if ( this .accountType !== 'Free' && ! this .companyName) e.companyName = 'Required' ; if ( this .accountType === 'Enterprise' ) { const s = this .seats; if (s == null || s < 5 || s > 1000 ) e.seats = 'Between 5 and 1000' ; } if ( this .subscribe && ! this .frequency) e.frequency = 'Required' ; if ( ! this .terms) e.terms = 'Required' ; return e; } private onSubmit ( ev : Event ) { ev. preventDefault (); this .errors = this . validate (); if ( ! Object. keys ( this .errors). length ) console. log ( this ); } render () { return html ` <form @submit=${ this . onSubmit }> <label>Email <input type="email" .value=${ this . email } @input=${ this . bind ( 'email' ) } /> ${ this . errors . email ? html `<span>${ this . errors . email }</span>` : null } </label> <label>Password <input type="password" .value=${ this . password } @input=${ this . bind ( 'password' ) } /> ${ this . errors . password ? html `<span>${ this . errors . password }</span>` : null } </label> <label>Account type <select .value=${ this . accountType } @change=${ this . bind ( 'accountType' ) }> <option>Free</option><option>Pro</option><option>Enterprise</option> </select> </label> ${ this . accountType !== 'Free' ? html ` <label>Company name <input .value=${ this . companyName } @input=${ this . bind ( 'companyName' ) } /> </label>` : null } ${ this . accountType === 'Enterprise' ? html ` <label>Seats <input type="number" .value=${ this . seats ?? ''} @input=${ this . bind ( 'seats' ) } /> </label>` : null } <label> <input type="checkbox" .checked=${ this . subscribe } @change=${ this . bind ( 'subscribe' ) } /> Subscribe to product updates </label> ${ this . subscribe ? html ` <label>Frequency <select .value=${ this . frequency } @change=${ this . bind ( 'frequency' ) }> <option>Daily</option><option>Weekly</option><option>Monthly</option> </select> </label>` : null } <label> <input type="checkbox" .checked=${ this . terms } @change=${ this . bind ( 'terms' ) } /> I accept the terms of service </label> <button type="submit">Sign up</button> </form> ` ; } }

< script setup lang = "ts" > import { useForm, useField } from 'vee-validate' ; import * as yup from 'yup' ; type Account = 'Free' | 'Pro' | 'Enterprise' ; const schema = yup. object ({ email: yup. string (). email (). required (), password: yup. string (). min ( 8 ). required (), accountType: yup. mixed < Account >(). oneOf ([ 'Free' , 'Pro' , 'Enterprise' ]). required (), companyName: yup. string (). when ( 'accountType' , { is : ( t : Account ) => t !== 'Free' , then : ( s ) => s. required (), }), seats: yup. number (). when ( 'accountType' , { is: 'Enterprise' , then : ( s ) => s. required (). min ( 5 ). max ( 1000 ), }), subscribe: yup. boolean (), frequency: yup. string (). when ( 'subscribe' , { is: true , then : ( s ) => s. required (), }), terms: yup. boolean (). oneOf ([ true ], 'Required' ), }); const { handleSubmit , errors } = useForm ({ validationSchema: schema, initialValues: { accountType: 'Free' , subscribe: false , terms: false }, }); const { value : email } = useField < string >( 'email' ); const { value : password } = useField < string >( 'password' ); const { value : accountType } = useField < Account >( 'accountType' ); const { value : companyName } = useField < string >( 'companyName' ); const { value : seats } = useField < number | null >( 'seats' ); const { value : subscribe } = useField < boolean >( 'subscribe' ); const { value : frequency } = useField < string >( 'frequency' ); const { value : terms } = useField < boolean >( 'terms' ); const onSubmit = handleSubmit (( vals ) => console. log (vals)); </ script > < template > < form @ submit = " onSubmit " > < label >Email < input v-model = " email " type = "email" /> < span v-if = " errors.email " >{{ errors.email }}</ span > </ label > < label >Password < input v-model = " password " type = "password" /> < span v-if = " errors.password " >{{ errors.password }}</ span > </ label > < label >Account type < select v-model = " accountType " > < option >Free</ option >< option >Pro</ option >< option >Enterprise</ option > </ select > </ label > < label v-if = " accountType !== 'Free'" >Company name < input v-model = " companyName " /> < span v-if = " errors.companyName " >{{ errors.companyName }}</ span > </ label > < label v-if = " accountType === 'Enterprise'" >Seats < input v-model . number = " seats " type = "number" min = "5" max = "1000" /> < span v-if = " errors.seats " >{{ errors.seats }}</ span > </ label > < label > < input v-model = " subscribe " type = "checkbox" /> Subscribe to product updates </ label > < label v-if = " subscribe " >Frequency < select v-model = " frequency " > < option >Daily</ option >< option >Weekly</ option >< option >Monthly</ option > </ select > </ label > < label > < input v-model = " terms " type = "checkbox" /> I accept the terms of service </ label > < button type = "submit" >Sign up</ button > </ form > </ template >

< form id = "signup" > < label >Email < input name = "email" type = "email" required /> </ label > < label >Password < input name = "password" type = "password" required minlength = "8" /> </ label > < label >Account type < select name = "accountType" required > < option >Free</ option >< option >Pro</ option >< option >Enterprise</ option > </ select > </ label > < label data-show = "companyName" hidden >Company name < input name = "companyName" /> </ label > < label data-show = "seats" hidden >Seats < input name = "seats" type = "number" min = "5" max = "1000" /> </ label > < label > < input name = "subscribe" type = "checkbox" /> Subscribe to product updates </ label > < label data-show = "frequency" hidden >Frequency < select name = "frequency" > < option >Daily</ option >< option >Weekly</ option >< option >Monthly</ option > </ select > </ label > < label > < input name = "terms" type = "checkbox" /> I accept the terms of service </ label > < button type = "submit" >Sign up</ button > </ form > < script > const f = document. querySelector ( '#signup' ); const show = ( key , on , req ) => { const el = f. querySelector ( `[data-show="${ key }"]` ); el.hidden = ! on; f.elements[key].required = on && req; }; function sync () { const t = f.accountType.value; show ( 'companyName' , t !== 'Free' , true ); show ( 'seats' , t === 'Enterprise' , true ); show ( 'frequency' , f.subscribe.checked, true ); } f.accountType. addEventListener ( 'change' , sync); f.subscribe. addEventListener ( 'change' , sync); sync (); f. addEventListener ( 'submit' , ( e ) => { e. preventDefault (); if ( ! f.terms.checked) { f.terms. setCustomValidity ( 'Required' ); } else { f.terms. setCustomValidity ( '' ); } if ( ! f. checkValidity ()) return f. reportValidity (); console. log (Object. fromEntries ( new FormData (f))); }); </ script >

import { gui } from '@golemui/gui-shared' ; import { GuiForm } from '@golemui/gui-react' ; const signupForm = [ gui.inputs. textInput ( 'email' , { label: 'Email' , validator: { required: true , format: 'email' }, }), gui.inputs. password ( 'password' , { label: 'Password' , validator: { required: true , minLength: 8 }, }), gui.inputs. dropdown ( 'accountType' , { label: 'Account type' , items: [ 'Free' , 'Pro' , 'Enterprise' ], defaultValue: 'Free' , validator: { type: 'string' , required: true }, }), gui.inputs. textInput ( 'companyName' , { label: 'Company name' , validator: { required: true }, include: { when: '$form.accountType !== "Free"' }, }), gui.inputs. numberInput ( 'seats' , { label: 'Seats' , validator: { required: true , minimum: 5 , maximum: 1000 }, include: { when: '$form.accountType === "Enterprise"' }, }), gui.inputs. checkbox ( 'subscribe' , { label: 'Subscribe to product updates' , }), gui.inputs. dropdown ( 'frequency' , { label: 'Frequency' , items: [ 'Daily' , 'Weekly' , 'Monthly' ], validator: { type: 'string' , required: true }, include: { when: '$form.subscribe === true' }, }), gui.inputs. checkbox ( 'terms' , { label: 'I accept the terms of service' , validator: { const: true }, }), gui.actions. button ({ label: 'Sign up' , actionType: 'submit' }), ]; export function SignupForm () { return ( < GuiForm config = {{ formDef: signupForm }} formSubmit = {( data ) => console. log (data)} /> ); }

import { Component } from '@angular/core' ; import { CommonModule } from '@angular/common' ; import { gui } from '@golemui/gui-shared' ; import { FormComponent } from '@golemui/gui-angular' ; const signupForm = [ gui.inputs. textInput ( 'email' , { label: 'Email' , validator: { required: true , format: 'email' }, }), gui.inputs. password ( 'password' , { label: 'Password' , validator: { required: true , minLength: 8 }, }), gui.inputs. dropdown ( 'accountType' , { label: 'Account type' , items: [ 'Free' , 'Pro' , 'Enterprise' ], defaultValue: 'Free' , validator: { type: 'string' , required: true }, }), gui.inputs. textInput ( 'companyName' , { label: 'Company name' , validator: { required: true }, include: { when: '$form.accountType !== "Free"' }, }), gui.inputs. numberInput ( 'seats' , { label: 'Seats' , validator: { required: true , minimum: 5 , maximum: 1000 }, include: { when: '$form.accountType === "Enterprise"' }, }), gui.inputs. checkbox ( 'subscribe' , { label: 'Subscribe to product updates' , }), gui.inputs. dropdown ( 'frequency' , { label: 'Frequency' , items: [ 'Daily' , 'Weekly' , 'Monthly' ], validator: { type: 'string' , required: true }, include: { when: '$form.subscribe === true' }, }), gui.inputs. checkbox ( 'terms' , { label: 'I accept the terms of service' , validator: { const: true }, }), gui.actions. button ({ label: 'Sign up' , actionType: 'submit' }), ]; @ Component ({ selector: 'app-signup' , imports: [CommonModule, FormComponent], template: `<gui-form [config]="config" (formSubmit)="onSubmit($event)"></gui-form>` , }) export class SignupForm { protected config = { formDef: signupForm }; onSubmit ( data : unknown ) { console. log (data); } }

import { LitElement, html } from 'lit' ; import { customElement } from 'lit/decorators.js' ; import { gui } from '@golemui/gui-shared' ; import '@golemui/gui-lit' ; const signupForm = [ gui.inputs. textInput ( 'email' , { label: 'Email' , validator: { required: true , format: 'email' }, }), gui.inputs. password ( 'password' , { label: 'Password' , validator: { required: true , minLength: 8 }, }), gui.inputs. dropdown ( 'accountType' , { label: 'Account type' , items: [ 'Free' , 'Pro' , 'Enterprise' ], defaultValue: 'Free' , validator: { type: 'string' , required: true }, }), gui.inputs. textInput ( 'companyName' , { label: 'Company name' , validator: { required: true }, include: { when: '$form.accountType !== "Free"' }, }), gui.inputs. numberInput ( 'seats' , { label: 'Seats' , validator: { required: true , minimum: 5 , maximum: 1000 }, include: { when: '$form.accountType === "Enterprise"' }, }), gui.inputs. checkbox ( 'subscribe' , { label: 'Subscribe to product updates' , }), gui.inputs. dropdown ( 'frequency' , { label: 'Frequency' , items: [ 'Daily' , 'Weekly' , 'Monthly' ], validator: { type: 'string' , required: true }, include: { when: '$form.subscribe === true' }, }), gui.inputs. checkbox ( 'terms' , { label: 'I accept the terms of service' , validator: { const: true }, }), gui.actions. button ({ label: 'Sign up' , actionType: 'submit' }), ]; @ customElement ( 'signup-form' ) export class SignupForm extends LitElement { private config = { formDef: signupForm }; override createRenderRoot () { return this ; } render () { return html ` <gui-form .config=${ this . config } @formSubmit=${ ( e : CustomEvent ) => console . log ( e . detail ) } ></gui-form> ` ; } }

< script setup lang = "ts" > import { gui } from '@golemui/gui-shared' ; import { GuiForm } from '@golemui/gui-vue' ; const signupForm = [ gui.inputs. textInput ( 'email' , { label: 'Email' , validator: { required: true , format: 'email' }, }), gui.inputs. password ( 'password' , { label: 'Password' , validator: { required: true , minLength: 8 }, }), gui.inputs. dropdown ( 'accountType' , { label: 'Account type' , items: [ 'Free' , 'Pro' , 'Enterprise' ], defaultValue: 'Free' , validator: { type: 'string' , required: true }, }), gui.inputs. textInput ( 'companyName' , { label: 'Company name' , validator: { required: true }, include: { when: '$form.accountType !== "Free"' }, }), gui.inputs. numberInput ( 'seats' , { label: 'Seats' , validator: { required: true , minimum: 5 , maximum: 1000 }, include: { when: '$form.accountType === "Enterprise"' }, }), gui.inputs. checkbox ( 'subscribe' , { label: 'Subscribe to product updates' , }), gui.inputs. dropdown ( 'frequency' , { label: 'Frequency' , items: [ 'Daily' , 'Weekly' , 'Monthly' ], validator: { type: 'string' , required: true }, include: { when: '$form.subscribe === true' }, }), gui.inputs. checkbox ( 'terms' , { label: 'I accept the terms of service' , validator: { const: true }, }), gui.actions. button ({ label: 'Sign up' , actionType: 'submit' }), ]; const config = { formDef: signupForm }; const onSubmit = ( data : unknown ) => console. log (data); </ script > < template > < GuiForm : config = " config " @ form-submit = " onSubmit " /> </ template >