CRUD / foldkit-mixins-crud

When the read fails

A failed read is said in a role="alert" paragraph. If the list already had rows (a refresh that failed), they stay on screen below it, because they are still the best answer there is. Remote does not retry a failed read on its own, so give onRetry a Message and the view adds a button that sends it:

ts
PostTable({ page: Posts.page(model), onRetry: Message.RetriedPosts() }, h)

// in update
case 'RetriedPosts':
  return { model: Posts.refresh(model) }

DetailView does the same with its value: the alert and the button above the description list, inside the same root.

The button leaves the page once the refresh starts, and with it the keyboard focus. The view cannot move focus itself: focus caused by a Message belongs to that Message. Return a Dom.focus Command from the retry's branch, which runs after the next render, and focus lands on the list instead of the page:

ts
case 'RetriedPosts':
  return { model: Posts.refresh(model), commands: [FocusPosts()] }

// FocusPosts: Command.define('FocusPosts', { messages: [CompletedFocusPosts],
//   execute: Dom.focus('#Posts', { makeFocusable: true }).pipe(
//     Effect.ignore, Effect.as(CompletedFocusPosts())) })

examples/entity does exactly this, and its page test checks where focus lands.