CRUD / foldkit-crud

A first list

A list is one query and one Selection: the query decides which rows, the Selection what to show of each.

ts
const Authors = Crud.list('Authors', {
  query: AuthorsQuery, // Query.make('Authors', { Input: { search }, Result: Query.connection(Blog.Author) })
  selection: Entity.select(Blog.Author, { id: true, name: true }),
  pageSize: 25,
  // How a row reads as a choice, for a list that feeds relation pickers.
  choice: { value: row => row.id, label: row => row.name },
})

const AuthorList = Authors.at({
  data: Data,
  // The query's input as the Model has it; none while the list is not shown.
  input: model => Option.map(Option.fromNullOr(model.search), search => ({ search })),
})

const subscriptions = Data.subscriptions({ authors: AuthorList.active })

What each call does:

  • Crud.list is a declaration: no request, no state. Authors.columns is the selected members in the Selection's order, each with its key, member, a label found the way a form finds one (the schema's title, else Form.label metadata, else the key), and a display.
  • Authors.at binds it to your Remote domain and your Model. It still does no I/O.
  • AuthorList.active is the requirement: while input gives a value, the page and its rows are fetched and retained. Another input is another connection, fetched on its own.
  • AuthorList.page(model) reads Remote's store: a RemoteData of the page, with items typed by the Selection, hasNext and hasPrevious.
  • AuthorList.more(model) is "load more": the Model with the list showing one page more, an Option, none when it already shows every row. Return it from update (Option.getOrElse(AuthorList.more(model), () => model)); the read entry fetches the rows it lacks.
  • AuthorList.refresh(model) is the Model with the list asked for again. A failed read is not retried on its own, so this is what a retry button's Message returns.

AuthorList.row(id) is a Projection of one row through the list's Selection, whether or not the query finds it now, and AuthorList.choiceOf(model, id) is that row as a choice once it is read. Relation pickers use both.

Wiring a page

Every placed piece has an active. Crud.actives gathers them from the pieces themselves, so a page cannot forget one:

ts
Data.wiring(Crud.actives({ posts: Posts, authors: Authors, editor: PostEditor, pickers }))

It takes a placed editor, list or detail, and the pickers from Crud.options, and gives { posts: Posts.active, ... }, for Data.wiring in a foldkit-bundle assembly, or for Data.subscriptions.