Advanced Form

All form elements of DuoyunUI are "controlled" elements. When editing a form, you need to dynamically assign values to the form elements, for example:

@customElement('my-ele')
export class MyEleElement extends GemElement {
  #state = createState({ name: '' });

  #onChange = (evt) => {
    this.#state(evt.detail);
  };

  render = () => {
    return html`
      <dy-form @change=${this.#onChange}>
        <dy-form-item name="name" .value=${this.#state.name}></dy-form-item>
      </dy-form>
    `;
  };
}

Form validation

Add the required attribute to the field and validate the form when submitting:

@customElement('my-ele')
export class MyEleElement extends GemElement {
  #state = createState({ name: '' });

  get #formEle() {
    return this.shadowRoot.querySelect('dy-form');
  }

  #onChange = (evt) => {
    this.#state(evt.detail);
  };

  #onSubmit = async () => {
    if (!(await this.#formEle.valid())) return;
    console.log('Submit!');
  };

  render = () => {
    return html`
      <dy-form @change=${this.#onChange}>
        <dy-form-item required name="name" .value=${this.#state.name}></dy-form-item>
      </dy-form>
      <dy-button @click=${this.#onSubmit}></dy-button>
    `;
  };
}

Using custom validator:

@customElement('my-ele')
export class MyEleElement extends GemElement {
  #state = createState({ name: '' });

  get #formEle() {
    return this.shadowRoot.querySelect('dy-form');
  }

  #onChange = (evt) => {
    this.#state(evt.detail);
  };

  #onSubmit = async () => {
    if (!(await this.#formEle.valid())) return;
    console.log('Submit!');
  };

  render = () => {
    return html`
      <dy-form @change=${this.#onChange}>
        <dy-form-item
          .rules=${[
            {
              validator: async () => {
                if (!this.#state.name) {
                  throw new Error('name is required');
                }
              },
            },
          ]}
          name="name"
          .value=${this.#state.name}
        ></dy-form-item>
      </dy-form>
      <dy-button @click=${this.#onSubmit}></dy-button>
    `;
  };
}

Custom form field

<dy-form-item> default support text, number, checkbox, switch, picker, radio, select, textarea, if them can't meet your needs, you can use your own elements, just implement the value attributes and bubble change event, then you can use <dy-form-item> slot type:

@customElement('my-ele')
export class MyEleElement extends GemElement {
  #state = createState({ name: '' });

  #onChange = (evt) => {
    this.#state(evt.detail);
  };

  render = () => {
    return html`
      <dy-form @change=${this.#onChange}>
        <dy-form-item name="name" type="slot" .value=${this.#state.name}>
          <my-input></my-input>
        </dy-form-item>
      </dy-form>
    `;
  };
}

If you don't want to write custom elements, you can also use existing DuoyunUI form elements to complete your needs:

@customElement('my-ele')
export class MyEleElement extends GemElement {
  #state = createState({ name: '', type: '' });

  #onChangeName = ({ detail }) => {
    this.#state({ name: detail });
  };

  #onChangeType = ({ detail }) => {
    this.#state({ type: detail });
  };

  render = () => {
    return html`
      <dy-form>
        <dy-form-item
          .rules=${
            [
              /** support validator */
            ]
          }
        >
          <dy-input-group>
            <dy-input .value=${this.#state.name} @change=${this.#onChangeName}></dy-input>
            <dy-input .value=${this.#state.type} @change=${this.#onChangeType}></dy-input>
          </dy-input-group>
        </dy-form-item>
      </dy-form>
    `;
  };
}