Lit Example: Ui Libraries

ts
import { LitElement, html } from 'lit'
import { customElement } from 'lit/decorators.js'
import '@material/web/textfield/filled-text-field.js'
import '@material/web/checkbox/checkbox.js'
import '@material/web/select/filled-select.js'
import '@material/web/select/select-option.js'
import '@material/web/button/filled-button.js'
import '@material/web/button/outlined-button.js'

import { TanStackFormController } from '@tanstack/lit-form'
import { repeat } from 'lit/directives/repeat.js'
import { styles } from './styles.js'

interface Employee {
  firstName: string
  lastName: string
  employed: boolean
  jobTitle?: string
}

@customElement('tanstack-form-demo')
export class TanStackFormDemo extends LitElement {
  static styles = styles

  #form = new TanStackFormController(this, {
    defaultValues: {
      employees: [] as Employee[],
    },
  })

  render() {
    return html`
      <form
        id="form"
        @submit=${(e: Event) => {
          e.preventDefault()
        }}
      >
        <h1>TanStack Form - Lit Demo</h1>
        ${this.#form.field(
          {
            name: 'employees',
            defaultValue: [],
          },
          (employeesField) => {
            return html`${repeat(
                employeesField.state.value,
                (_, index) => index,
                (_, index) => {
                  return html`
                    ${this.#form.field(
                      {
                        name: `employees[${index}].firstName`,
                        validators: {
                          onChange: ({ value }: { value: string }) => {
                            return value && value.length < 3
                              ? 'Not long enough'
                              : undefined
                          },
                        },
                      },
                      (field) => {
                        return html` <div>
                          <label>First Name</label>
                          <md-filled-text-field
                            type="text"
                            placeholder="First Name"
                            .value="${field.state.value}"
                            @blur="${() => field.handleBlur()}"
                            @input="${(e: Event) => {
                              const target = e.target as HTMLInputElement
                              field.handleChange(target.value)
                            }}"
                            .error="${!!(
                              field.state.meta.isTouched &&
                              !field.state.meta.isValid
                            )}"
                            .errorText="${field.state.meta.errors.join(', ')}"
                          ></md-filled-text-field>
                        </div>`
                      },
                    )}
                    ${this.#form.field(
                      { name: `employees[${index}].lastName` },
                      (lastNameField) => {
                        return html` <div>
                          <label>Last Name</label>
                          <md-filled-text-field
                            type="text"
                            placeholder="Last Name"
                            .value="${lastNameField.state.value}"
                            @blur="${() => lastNameField.handleBlur()}"
                            @input="${(e: Event) => {
                              const target = e.target as HTMLInputElement
                              lastNameField.handleChange(target.value)
                            }}"
                            .error="${!!(
                              lastNameField.state.meta.isTouched &&
                              !lastNameField.state.meta.isValid
                            )}"
                            .errorText="${lastNameField.state.meta.errors.join(
                              ', ',
                            )}"
                          ></md-filled-text-field>
                        </div>`
                      },
                    )}
                    ${this.#form.field(
                      { name: `employees[${index}].employed` },
                      (employedField) => {
                        return html`<div>
                            <label>Employed?</label>
                            <md-checkbox
                              type="checkbox"
                              @input="${() =>
                                employedField.handleChange(
                                  !employedField.state.value,
                                )}"
                              .checked="${employedField.state.value}"
                              @blur="${() => employedField.handleBlur()}"
                            ></md-checkbox>
                          </div>
                          ${employedField.state.value
                            ? this.#form.field(
                                {
                                  name: `employees[${index}].jobTitle`,
                                  defaultValue: '',
                                  validators: {
                                    onChange: ({ value }) => {
                                      return value?.length === 0
                                        ? 'Needs to have a job here'
                                        : null
                                    },
                                  },
                                },
                                (jobTitleField) => {
                                  return html` <div>
                                    <label>Job Title</label>
                                    <md-filled-text-field
                                      type="text"
                                      placeholder="Job Title"
                                      .value="${jobTitleField.state.value}"
                                      @blur="${() =>
                                        jobTitleField.handleBlur()}"
                                      @input="${(e: Event) => {
                                        const target =
                                          e.target as HTMLInputElement
                                        jobTitleField.handleChange(target.value)
                                      }}"
                                      .error="${!jobTitleField.state.meta
                                        .isValid}"
                                      .errorText="${jobTitleField.state.meta.errors.join(
                                        ', ',
                                      )}"
                                    ></md-filled-text-field>
                                  </div>`
                                },
                              )
                            : ''} `
                      },
                    )}
                  `
                },
              )}

              <div>
                <md-outlined-button
                  type="button"
                  @click=${() => {
                    employeesField.pushValue({
                      firstName: '',
                      lastName: '',
                      employed: false,
                    })
                  }}
                >
                  Add employee
                </md-outlined-button>
              </div> `
          },
        )}

        <div>
          <md-filled-button
            type="submit"
            ?disabled=${this.#form.api.state.isSubmitting}
          >
            ${this.#form.api.state.isSubmitting ? html` Submitting` : 'Submit'}
          </md-filled-button>
          <md-outlined-button
            type="button"
            id="reset"
            @click=${() => {
              this.#form.api.reset()
            }}
          >
            Reset
          </md-outlined-button>
        </div>
      </form>
      <pre>${JSON.stringify(this.#form.api.state, null, 2)}</pre>
    `
  }
}
import { LitElement, html } from 'lit'
import { customElement } from 'lit/decorators.js'
import '@material/web/textfield/filled-text-field.js'
import '@material/web/checkbox/checkbox.js'
import '@material/web/select/filled-select.js'
import '@material/web/select/select-option.js'
import '@material/web/button/filled-button.js'
import '@material/web/button/outlined-button.js'

import { TanStackFormController } from '@tanstack/lit-form'
import { repeat } from 'lit/directives/repeat.js'
import { styles } from './styles.js'

interface Employee {
  firstName: string
  lastName: string
  employed: boolean
  jobTitle?: string
}

@customElement('tanstack-form-demo')
export class TanStackFormDemo extends LitElement {
  static styles = styles

  #form = new TanStackFormController(this, {
    defaultValues: {
      employees: [] as Employee[],
    },
  })

  render() {
    return html`
      <form
        id="form"
        @submit=${(e: Event) => {
          e.preventDefault()
        }}
      >
        <h1>TanStack Form - Lit Demo</h1>
        ${this.#form.field(
          {
            name: 'employees',
            defaultValue: [],
          },
          (employeesField) => {
            return html`${repeat(
                employeesField.state.value,
                (_, index) => index,
                (_, index) => {
                  return html`
                    ${this.#form.field(
                      {
                        name: `employees[${index}].firstName`,
                        validators: {
                          onChange: ({ value }: { value: string }) => {
                            return value && value.length < 3
                              ? 'Not long enough'
                              : undefined
                          },
                        },
                      },
                      (field) => {
                        return html` <div>
                          <label>First Name</label>
                          <md-filled-text-field
                            type="text"
                            placeholder="First Name"
                            .value="${field.state.value}"
                            @blur="${() => field.handleBlur()}"
                            @input="${(e: Event) => {
                              const target = e.target as HTMLInputElement
                              field.handleChange(target.value)
                            }}"
                            .error="${!!(
                              field.state.meta.isTouched &&
                              !field.state.meta.isValid
                            )}"
                            .errorText="${field.state.meta.errors.join(', ')}"
                          ></md-filled-text-field>
                        </div>`
                      },
                    )}
                    ${this.#form.field(
                      { name: `employees[${index}].lastName` },
                      (lastNameField) => {
                        return html` <div>
                          <label>Last Name</label>
                          <md-filled-text-field
                            type="text"
                            placeholder="Last Name"
                            .value="${lastNameField.state.value}"
                            @blur="${() => lastNameField.handleBlur()}"
                            @input="${(e: Event) => {
                              const target = e.target as HTMLInputElement
                              lastNameField.handleChange(target.value)
                            }}"
                            .error="${!!(
                              lastNameField.state.meta.isTouched &&
                              !lastNameField.state.meta.isValid
                            )}"
                            .errorText="${lastNameField.state.meta.errors.join(
                              ', ',
                            )}"
                          ></md-filled-text-field>
                        </div>`
                      },
                    )}
                    ${this.#form.field(
                      { name: `employees[${index}].employed` },
                      (employedField) => {
                        return html`<div>
                            <label>Employed?</label>
                            <md-checkbox
                              type="checkbox"
                              @input="${() =>
                                employedField.handleChange(
                                  !employedField.state.value,
                                )}"
                              .checked="${employedField.state.value}"
                              @blur="${() => employedField.handleBlur()}"
                            ></md-checkbox>
                          </div>
                          ${employedField.state.value
                            ? this.#form.field(
                                {
                                  name: `employees[${index}].jobTitle`,
                                  defaultValue: '',
                                  validators: {
                                    onChange: ({ value }) => {
                                      return value?.length === 0
                                        ? 'Needs to have a job here'
                                        : null
                                    },
                                  },
                                },
                                (jobTitleField) => {
                                  return html` <div>
                                    <label>Job Title</label>
                                    <md-filled-text-field
                                      type="text"
                                      placeholder="Job Title"
                                      .value="${jobTitleField.state.value}"
                                      @blur="${() =>
                                        jobTitleField.handleBlur()}"
                                      @input="${(e: Event) => {
                                        const target =
                                          e.target as HTMLInputElement
                                        jobTitleField.handleChange(target.value)
                                      }}"
                                      .error="${!jobTitleField.state.meta
                                        .isValid}"
                                      .errorText="${jobTitleField.state.meta.errors.join(
                                        ', ',
                                      )}"
                                    ></md-filled-text-field>
                                  </div>`
                                },
                              )
                            : ''} `
                      },
                    )}
                  `
                },
              )}

              <div>
                <md-outlined-button
                  type="button"
                  @click=${() => {
                    employeesField.pushValue({
                      firstName: '',
                      lastName: '',
                      employed: false,
                    })
                  }}
                >
                  Add employee
                </md-outlined-button>
              </div> `
          },
        )}

        <div>
          <md-filled-button
            type="submit"
            ?disabled=${this.#form.api.state.isSubmitting}
          >
            ${this.#form.api.state.isSubmitting ? html` Submitting` : 'Submit'}
          </md-filled-button>
          <md-outlined-button
            type="button"
            id="reset"
            @click=${() => {
              this.#form.api.reset()
            }}
          >
            Reset
          </md-outlined-button>
        </div>
      </form>
      <pre>${JSON.stringify(this.#form.api.state, null, 2)}</pre>
    `
  }
}
Subscribe to Bytes

Your weekly dose of JavaScript news. Delivered every Monday to over 100,000 devs, for free.

Bytes

No spam. Unsubscribe at any time.