Grid View composition cookbook
Grid View composition cookbook
Choose who owns a value
| Need | Owner and authoring form |
|---|---|
| Shared business state or a query parameter | A model input; calculation remains in native |
| A viewer's temporary selection or disclosure | A View local |
| Browser formatting or an event adapter | Explicit JavaScript glue with USING bridges |
| A reusable component requesting a change | An explicit action(...) argument |
| A reusable component editing a value directly | An edit argument bound to a writable input or local |
A Grid expression reading private state still needs the existing explicit model/private bridge. Scoped slots do not add a browser evaluator. Put shared filter criteria in model inputs and derive the filtered rows in Grid; private filtering of resident model tables still needs an explicit supported bridge.
Make a read-only summary
ELEMENT AmountSummary(amount: number, title: string = "Amount")
Text title
show amount
ENDA typed parameter is read-only unless declared edit. Passing a model input to
this component does not turn its summary into an editor. Direct writes and
forwarding to an edit parameter are refused. Use a separate explicit callback
when the caller should decide how a requested change is performed.
Send a request to the caller
ELEMENT ApproveButton(amount: number, approve: action(number))
"Approve" -> approve(amount)
ENDPass a named action with one parameter. Known incompatible argument types are refused, including through a forwarded callback. The caller-owned callback keeps the permissions of the scope that supplied it; it cannot recover write access that its own enclosing component already surrendered.
Read restrictions currently follow the underlying binding through aliases within the component. Avoid passing the same value as both a read and an edit argument; use one edit argument when the component intentionally needs both operations. A contract is an authoring guarantee, not a security sandbox for trusted code.
Let a caller customize a row
ELEMENT Listing(rows: array)
FOR entry IN rows key entry.id
BODY "row"(entry)
END
Rows = [{id: "a", amount: 2}, {id: "b", amount: 3}]
SURFACE Desk
Listing(Rows)
SLOT "row"(record)
show record.amount * 2
ENDThe row expression lowers through the existing native row path. Slot aliases are positional and local to the supplied content. Use the same argument count for each placement of a named BODY. Nested component declarations own their own slots. A slot can accept up to 64 arguments; duplicate aliases and unknown fills are refused.
Keep page drafts separate while sharing caller state
A local inside a component belongs to that component instance. A slot fill
keeps the scope of its caller: an unqualified caller local remains shared even
when the component has a private local with the same name. A nested component
supplied in the slot gets its own instance state. Navigating between pages
retains those drafts for the current View lifetime.
The scoped-slot page example
puts one Panel and one nested Draft on each page. Editing a panel or nested draft
leaves the other page's component drafts unchanged. Editing Caller first or
Caller second changes their one shared caller note. The caption slot parameter
is available only within the fill; it cannot escape to page siblings.
Use Record interactions as a CHECK in a private copy to capture initialized component drafts and the current page. Add reviewed value and page expectations, then navigate and verify the retained owners. CHECK setup uses the compiler's exact component-local identities. Source packages preserve the Panel's private state and scoped BODY interface; importing under a new name creates new compiled instance identities while the receiving model continues to own its slot fills.
Share a component without losing its contract
Use .gridcomponent for source composition, named callbacks, private instance
state, defaults and scoped slots. The package's versioned interface describes
parameter modes, types, defaults and slot fill signatures. Import compares it
with source and refuses disagreement. Legacy source packages remain supported.
The compiled App template format remains suitable for its existing simpler
binding and slot subset.
Review and check a change
Review the inspector's behavior changes after editing accepted source. The comparison reports reads, direct/action write targets, trusted-code requirements and changes in presentation for labels that are unique on both sides. It is not a complete semantic diff; duplicate labels are deliberately left unmatched.
Use a private copy for interaction. Save CHECK setup retains the staged inputs or locals. Add interactions and expectations in Code and run the CHECK. The private preview also offers the reviewed recording draft described below; supported traces can be replayed in fresh native copies. Unsupported gestures refuse capture, and third-party portal behavior remains under review.
Share record validation
Put common FIELD rules in a RECORD CONTRACT, then apply it with
VALIDATE RECORD Order USING OrderRules. Compose another contract with
INCLUDE AddressRules AT shipping to reuse its rules under that member.
The record-contract reference
and runnable example
show two owners sharing nested address rules and a live model bound.
Use Field rules → Shared rules to review a local contract's model, private
and transitive INCLUDE uses, create a definition, or edit its FIELD/INCLUDE body.
The compiler rechecks the complete candidate draft and returns one source edit
with an exact inverse. Imported definitions show their origin and remain read
only; a used definition cannot be removed. A native refusal retains the body
draft for review. Native integration verification remains open in the roadmap.
Inside a View or element, apply the same contract to
a record local with VALIDATE RECORD Draft USING OrderRules. Its operands must
be literal. Each component instance keeps its own private owner; rejected writes
retain the draft. For a private list of records, VALIDATE ROWS Lines USING LineRules checks every row; see the
row contract example.
At model scope, the same VALIDATE ROWS syntax checks an input collection,
including exact nested paths such as Order.lines, against live proposed-batch
bounds. The model row example
shows two tables sharing a contract. To import reusable rules, use
USE "rules.gs" AS Rules and Rules.LineRules, or expose LineRules directly.
Private includes and immutable library bounds stay attached; caller variables
cannot replace them. See the imported example.
A View library can keep those rules private to its Elements and declare a public
wrapper contract for model owners. Private drafts resolve rules in the defining
library, even when the caller has a same-named contract. See the
View-library example and
editor source. Keep the
source assets together when sharing a library. For a standalone component,
choose Grid source component export to retain its selected private record/row
rules, nested includes and closed library bounds. Import renames the component's
rules together; existing receiving-model input rules stay with their owners.
The packaged example
shows two independent drafts after source import.
Compose repeated rows through scoped content
A list component can supply its current row and title to a slot. The receiving
View supplies a nested editor and a caller-owned field in that fill. In the
repeated scoped-slot example,
two listings reuse row keys a and b. Each nested Draft keeps a separate note;
the Listing notes are instance-owned, and the caller note is shared by every
fill. A key identifies a row inside its enclosing listing, not globally.
Use CONCAT or & to build a label from the supplied title and row fields.
Choose distinct accessible labels so recording can address each editor. A
recorded CHECK retains the compiler's local declaration and row occurrence;
keep those generated addresses when reviewing or editing its setup. Source
component export retains the list's read parameters and scoped-slot interface,
while the receiving model owns the supplied fill and nested editor.
Change a component's optional arguments
In Properties, select an omitted argument and enter its value. Adding a later argument writes any intervening defaults explicitly, so positional arguments keep their meaning. Remove the last supplied optional argument to restore its default. Remove later arguments first when you want to omit an earlier one. Comments and caller content stay with the call.
Rename a record field with its uses
Select a record in Properties, choose its field and review the reference links. If a repeated control offers more than one Validation owner, choose the private record before editing its fields. Tree row slots and pages retain their own local declaration. Same-named row or slot parameters remain separate. Shared-contract keys require reviewing the linked rule and its uses in Code; a computed default value keeps its source definition link. Rename applies one source edit containing the declaration, proven member uses, and model field-validation targets and operands. Nested paths, plain aliases and private locals retain their owners. Unresolved uses prevent a partial edit; open their links to review dynamic keys, shared component uses, whole-record writes, CHECK record fixtures/comparisons or embedded code. A CHECK reset and an unrelated record fixture retain their source. Code Undo restores the entire transaction. Renaming source keys does not migrate previously stored values.
See the reference for the complete supported surface and the first View tutorial for a runnable composition.
Turn a private preview interaction into a CHECK draft
Start a fresh private copy, open Record interactions as a CHECK, and choose Start recording. The recorder stages model inputs, ordinary View locals and the current page. Try uniquely named buttons, text fields, Enter commits, Escape cancellation and accessible choices in the preview. Supported named choices become presses or picks; supported choice-navigation keys retain their order. Use Expect current value, Expect current page, or Review current controls to add expectations you have inspected.
For repeated rows, recording keeps each initialized private draft's exact
occurrence address, including retained off-page state. Show a row before adding
or replaying an expectation for its private state. Preserve generated
local "declaration" in row "captured instance" targets when editing the CHECK;
they do not mean the first or second row on screen.
Choose Review recording to pause the preview, inspect the ordered steps, name the CHECK and Save recording in Code. The compiler checks the exact source draft and inserts one CHECK. Code Undo reverses the insertion. Run the CHECK in a fresh copy before relying on it; saving source does not prove replay.
An ambiguous or unsupported gesture makes the recording incomplete and prevents saving it. Reset the copy and try again, or describe the CHECK in Code. Trusted scripts or custom glue, unsupported choice gestures, rule-time changes and unproven blank model defaults need explicit Code authoring. Earlier actions and external effects are not reconstructed from the staged inputs.
Reuse a resident table with private filters
Declare source: read table on an Element that displays an existing resident
table. Keep its filter in a local, then use that local in FILTER(source, ...).
Each instance owns its filter and paging state, so changing one instance does
not change the other or write a model input. An updated filter returns that
instance to its first page; a late response from its previous query is ignored.
See two private order filters. Import an Orders table with text customer and status fields before running it. The read interface does not grant row edits. Declare separate actions when the component needs to request model changes. Source packages preserve this table interface and check the receiving binding when imported.