CxJS

Form Edit

Grid can be connected to a separate form for editing selected records. This pattern is useful when records have many fields or when you want a clear separation between viewing and editing.

<div controller={PageController}>
  <Grid
    records={m.records}
    selection={{ type: KeySelection, bind: m.selectedId, keyField: "id" }}
    columns={[
      { header: "Name", field: "fullName", sortable: true },
      { header: "Phone", field: "phone" },
      { header: "City", field: "city", sortable: true },
      {
        header: "Notified",
        field: "notified",
        value: expr(m.$record.notified, (n) => (n ? "Yes" : "No")),
      },
      {
        header: "Actions",
        style: "padding: 2px",
        children: (
          <Button
            mod="hollow"
            onMouseDown={stopPropagation}
            onClick={(e, instance) => {
              const id = instance.store.get(m.$record.id)
              instance.getControllerByType(PageController).removeRecord(id)
            }}
          >
            Remove
          </Button>
        ),
      },
    ]}
    recordAlias={m.$record}
  />

  <p class="mt-4">
    <Button
      onClick={(e, instance) =>
        instance.getControllerByType(PageController).addRecord()
      }
    >
      Add
    </Button>
  </p>

  <hr class="my-6" />

  <LabelsLeftLayout visible={hasValue(m.form)}>
    <strong text={m.form.fullName} class="mb-4" />
    <TextField label="Name" value={m.form.fullName} />
    <TextField label="Phone" value={m.form.phone} />
    <TextField label="City" value={m.form.city} />
    <Checkbox label="Notified" value={m.form.notified} />
    <Button
      onClick={(e, instance) =>
        instance.getControllerByType(PageController).saveRecord()
      }
    >
      Save
    </Button>
  </LabelsLeftLayout>
</div>
NamePhoneCityNotifiedActions
Alice Johnson555-0101New YorkYes
Bob Smith555-0102Los AngelesNo
Carol White555-0103ChicagoYes
David Brown555-0104HoustonNo
Eva Green555-0105PhoenixYes


Click a row to select it. The form below displays the selected record’s data for editing.

Loading Form Data

Use a trigger to load the selected record into the form:

this.addTrigger("loadForm", [m.selectedId, m.records], (id, records) => {
  const record = records?.find((r) => r.id === id);
  this.store.set(m.form, record || null);
});

Saving Changes

Update the records array when the form is saved using updateArray:

import { updateArray } from "cx/data";

saveRecord() {
  const form = this.store.get(m.form);
  if (!form) return;
  this.store.update(m.records, (records) =>
    updateArray(records, () => form, (r) => r.id === form.id)
  );
}

Preventing Row Selection on Button Click

When using action buttons inside a selectable grid, clicking the button also selects the row. Use stopPropagation on onMouseDown to prevent this:

import { stopPropagation } from "cx/util";

{
  header: "Actions",
  children: (
    <Button
      onMouseDown={stopPropagation}
      onClick={(e, instance) => {
        instance.getControllerByType(PageController).removeRecord(id);
      }}
    >
      Remove
    </Button>
  ),
}

When to Use

Form editing is ideal when:

  • Records have many fields that don’t fit in a grid
  • You want explicit Save/Cancel actions
  • Complex validation is needed before saving
  • The form needs additional context or related data

See also: Cell Editing, Row Editing, Inline Edit