Skip to content

Mapped Category

mapped-category is for model inputs that are awkward for users to edit directly. Use onehot-category instead when each option only activates one 0/1 model column.

The master field renders like a category dropdown. When the value changes, MLForm writes each mapped value into its target field.

User selects "Red"
-> mapping: { is_red: 1, is_green: 0, is_blue: 0 }
-> subordinate fields update in one batch

By default, the master mapped-category value is not submitted. The backend receives the subordinate fields through their own mappedTo values. Set includeInSubmission: true on the master only when the backend also needs the selected label/value.

const schema = {
fields: [
{
kind: "mapped-category",
id: "color",
label: "Color",
required: true,
options: [
{ label: "Red", value: "red", mapping: { is_red: 1, is_green: 0, is_blue: 0 } },
{ label: "Green", value: "green", mapping: { is_red: 0, is_green: 1, is_blue: 0 } },
{ label: "Blue", value: "blue", mapping: { is_red: 0, is_green: 0, is_blue: 1 } },
],
},
{ kind: "number", id: "is_red", label: "is_red", mappedTo: "is_red", hidden: true, inactiveFieldPolicy: "include" },
{ kind: "number", id: "is_green", label: "is_green", mappedTo: "is_green", hidden: true, inactiveFieldPolicy: "include" },
{ kind: "number", id: "is_blue", label: "is_blue", mappedTo: "is_blue", hidden: true, inactiveFieldPolicy: "include" },
{ kind: "number", id: "size", label: "Size (cm)", min: 1, max: 100, required: true },
],
reports: [{ kind: "classifier" }],
};

When the user selects “Green”, submission includes:

{ "is_red": 0, "is_green": 1, "is_blue": 0, "size": 42 }

Every option must be an object with label, value, and mapping.

Property Type Meaning
label string Text shown in the dropdown.
value string Stored value for the master field.
mapping Record<string, unknown> Internal target field ids and values to write.

Plain string options are not valid for mapped-category.

Fields referenced in mapping are subordinate fields. They usually should be hidden and submitted:

{
kind: "number",
id: "is_red",
label: "is_red",
mappedTo: "is_red",
hidden: true,
inactiveFieldPolicy: "include",
}

Without inactiveFieldPolicy: "include", hidden subordinate values are omitted from the payload.

Subordinate fields can use any compatible field type. For example, a preset can map to a hidden category field if the mapped value is one of that field’s allowed options.

MLForm checks mappings in two places:

Time Check
schema normalization Every mapping target id exists in the schema.
value apply Each mapped value passes the target field’s coercion and validation.

Example setup failure:

SchemaNormalizationError: mapped-category "color": mapping references unknown field "is_purple".

Example runtime failure:

EngineError: mapped-category "color": value "hello" invalid for "is_red": Expected a number.

Mapping also runs when host code sets the master value:

form.setValues({ color: "green" });

After that call, is_red, is_green, and is_blue contain the mapped values.

  • Mapping is one-way: master to subordinate fields.
  • Changing a subordinate field does not update the master.
  • Writes happen in one batch, so subscribers see one state change.
  • mapped-category is excluded from submission by default.
  • Target field ids are validated during schema normalization.
  • Target values are validated at runtime.
  • Avoid mapping one mapped-category into another.