Skip to content

Form-Associated Rating Input

Problem

Implement a form-associated rating input using defineField with a signal for two-way form binding.

Solution

ts
import { define, defineField, html, signal } from '@vielzeug/craftit';

define('rating-input', {
  formAssociated: true,
  setup() {
    const value = signal(0);

    // Pass the writable signal directly — defineField accepts Signal<T> or ReadonlySignal<T>.
    // A default toFormValue is applied: String(v) for primitives, null for null/undefined.
    const field = defineField({ value });

    return () => html`
      <button @click=${() => (value.value = 1)}>1</button>
      <button @click=${() => (value.value = 2)}>2</button>
      <button @click=${() => (value.value = 3)}>3</button>
      <button @click=${() => field.reportValidity()}>Validate</button>
      <p>Current: ${value}</p>
    `;
  },
});

With custom serialisation

ts
import { define, defineField, html, signal } from '@vielzeug/craftit';

define<{ disabled?: boolean }>('rating-input-v2', {
  formAssociated: true,
  props: { disabled: false },
  setup(props) {
    const value = signal<number[]>([]);

    const field = defineField({
      disabled: props.disabled,
      value,
      toFormValue: (v) => v.join(','),
    });

    return () => html`
      <button
        ?disabled=${props.disabled}
        @click=${() => field.setCustomValidity(value.value.length === 0 ? 'Please select a rating' : '')}>
        Validate
      </button>
    `;
  },
});