workspaces / workspace-xml-4Markdown ↓

Forms and relationships

Use a GraphQL form when its transaction maps directly to one mutation. The compiler reads the mutation's input types and supported validation directives, derives the field contracts, and verifies paths and requiredness. The form supplies variables with those exact paths. Lua is not required for this case.

Scalars a form can edit

Built-in GraphQL String, ID, Boolean, Int and Float inputs have known form shapes. A custom scalar must expose @iac_scalar(carries:) in the instance schema. The supported carries values are STRING, NUMBER, BOOLEAN, JSON_OBJECT, JSON_ARRAY and JSON; they describe its wire value, not a replacement for server validation. Business ID scalars, CurrencyISO and ISO8601 carry strings in the current server schema. Enum choices are derived from the enum definition.

An unannotated custom scalar fails with Custom scalar <name> needs @iac_scalar(carries:) for forms. The diagnostic identifies the selected input path and its XML field. Check the scalar's declaration in your instance schema instead of assuming every custom scalar is a string. If a scalar you need lacks this metadata, contact CoCoCo support rather than relabelling it to bypass validation.

<workspace id="customer-create" title="Create customer" role="Customer service" xmlns:t="urn:cococo:dataxml">
  <panel id="main" title="Customer">
    <query type="graphql">query { __typename }</query>
    <form id="customer" mode="create">
      <submit type="graphql" errors="createCustomer.errors">
        <![CDATA[mutation($input:CreateCustomerInput!){createCustomer(input:$input){errors{path message}}}]]>
      </submit>
      <field path="input.name" label="Customer name"/>
      <field path="input.email" label="Email" control="email"/>
      <form-actions save-label="Create customer"/>
    </form>
  </panel>
</workspace>

For editing, identify the record and its identity field, then map each editable input to its record-relative source. Hidden values can supply IDs. Changed-only fields are useful for patches, but a required input cannot be made optional merely by changing its submission policy. Omission and explicit null are different; clearing must be allowed by the server contract.

A field or hidden value with state reads declared Workspace state into the form input. Editing the form changes its draft, not that Workspace state. Use a filter to edit query state and refetch; its Apply action does not submit a form.

Select fields by declaring the fields the author should edit. The compiler checks that this selection can still supply all required inputs. Use the record-editor composite when selection, loading, identity and editing form one workflow.

Relationship fields can use static options or a lookup backed by another panel. A record picker stores its declared value, while displaying its declared label. The lookup items path is relative to its source panel result; value and label are paths relative to each item, without an item alias. For connection edges, node.id and node.name are typical item paths when those fields are selected. A root-level data-source can supply these options without a visible panel. It requires an id and one query, accepts an optional title and wait-for, and participates in ordinary loading and refresh. Point the lookup's panel attribute to that ID. It cannot contain display blocks. This removes visual clutter; it does not hide the returned data from an authorized Workspace reader. Loading options is a UI convenience, not authorization to reference every record. The server validates the relationship and current user's permissions on save.

The session retains drafts, touched state and validation errors. It prevents simultaneous duplicate saves, protects later edits from older responses, and separates unsaved work from the current submittable projection. Reset is an explicit discard operation and is disabled while submission is in flight.

Bind the mutation's error collection and, when present, its canonical result. Map server errors to input paths. Canonical fields acknowledge only the submitted transaction; a missing submitted field is not a successful canonical save. For compound business operations use a Lua action with the same form lifecycle, as shown in server Lua.

Confirmation and refresh

After validating the draft, the CoCoCo app captures the submission and asks the user to confirm it. The optional submit confirm attribute supplies the message. When omitted, the message is the workspace title followed by : confirm change. Whether a dialog appears depends on the app's confirmation settings; omitting the attribute does not disable confirmation.

Cancelling sends no mutation and retains the draft for editing. If confirmation fails, the form returns to a recoverable error state. Edits made while confirmation is open do not change the captured submission. After a successful domain response, the engine acknowledges the submitted values and refreshes panels. Validation, transport and business errors do not trigger that success refresh.

The app's and each panel's Refresh controls explicitly reload query data even when arguments have not changed. Panels waiting for selection remain idle. Refreshing does not silently discard a dirty draft: a changed record is offered as a pending update with the existing keep-editing/discard choice. XML does not currently configure polling or subscription topics; do not invent a panel refresh attribute.

Published platform manual · revision d72151c92b90