Form / foldkit-form

Nested input

A key mapped with Relation.nested holds the relation's target, not an id. A nested form is a form: the key holds rows, each a Model of the form built from the nested input.

ts
const NewComment = Entity.input(Blog.Comment, Schema.Struct({ body: Schema.String }))
const CreatePost = Entity.input(
  Blog.Post,
  Schema.Struct({ title: Schema.String, comments: Schema.Array(NewComment.schema) }),
  { comments: Relation.nested(Blog.Post.relations.comments, NewComment) },
)

// The form that edits a comment alone is the form a post's form nests.
const CommentForm = Form.make('Comment', NewComment, { inputs: { body: Input.multiline() } })
const PostForm = Form.make('PostForm', CreatePost, { nested: { comments: CommentForm } })

PostForm.row('comments', 'r0').Changed({ key: 'body', value: 'First' }) // a Message of PostForm
PostForm.nested.comments // CommentForm

PostForm.Message.RowAdded({ key: 'comments' })
PostForm.Message.RowRemoved({ key: 'comments', row: 'r0' })
PostForm.rows(PostForm.initial, 'comments') // [{ id, model }], each a Model of the nested form
The key isRows
a one whose schema must be thereexactly one, from the start; it cannot be removed
a one that admits null or undefinednone or one; none submits that nothing
a manyany number, starting with none
  • The nested form is one you make and pass (nested: { comments: CommentForm }), from the same input Relation.nested was given. The form that edits a comment alone is the form a post nests, with its controls, checks and words. A nested key given none gets a plain form of its input, with this form's messages and debounce. A form of another input throws.
  • A row is addressed with types. PostForm.row('comments', id) has the nested form's own constructors (Changed, Blurred, Searched, RowAdded, ...), each giving PostForm's Message, and send(message) for one already made, which is how a row inside a row is reached. PostForm.nested.comments is the form itself. Underneath it is Message.Nested({ key, row, message }). A Message for a row that is gone, or that is not one of the nested form, is dropped. A row's id is never reused.
  • A nested key's control is of kind Nested, with data { cardinality, optional, form } (narrow it with Input.Nested.is(control)); form is the nested form, with its own controls, field, and rows. Nested keys are in model.rows, not model.fields.
  • A submit validates every row too and shows every failure; it waits for a check running in a row as it does for its own. A rule on the list itself (Schema.isMaxLength(5)) is a failure of the whole, in errors.
  • fill fills rows from values, and nesting goes as deep as the inputs do.