commands

{
  id: "assign-person-to-study",
  url: "/commands/:id",
  method: "POST",
  fields: [
    { key: "study_id", type: "integer" },
    { key: "title", type: "string", select: true, collection: ["Dr", "Mr", "Mrs", "Miss"], validate: { presence: true, included: :collection } },
    { key: "first_name", type: "string", validate: { presence: true, length: 255 } },
    { key: "last_name", type: "string", validate: { presence: true, length: 255 } },
    { key: "email_address", type: "email", label: "Email", validate: { presence: true, uniquness: true } },
    { key: "phone_number", type: "text", label: "Phone", validate: { presence: true } },
    { key: "category_id", type: "integer", select: true, label: "Category", collection: { url: 'people/categories', label: '#name', value: '#id' }, validate: { uniqueness: true } }
  ]
}

We could have basic types, string, integer etc. or richer types such as email which imply certain validations with regards to the shape of the data, i.e. a regex.

The url is where to submit the command too, in this case /commands/:id is a generic controller. Note that :id refers to the command, “assign-person-to-study”, not a record id. The URL could also contain references to any of the form data, for example: “/organisation/{{ organisation_id }}/people”.

In the case of category_id the collection (possible selections) are to be fetched by the UI from the given URL, a JSON document is expected back and the label and value are the keys within the document to use, they could be specifyed as xpath or css selectors.

The collection will determin the possible values for a field (it will usually, but does not have to be, be rendered as select or autocomplete). Possible configurations to collection include:

static source

collection: { values: ["a", "b", "c"] }
collection: { values: { "a" => "1", "a" => 2, "c" => 3 }

HTTP source

collection: { url: "http://od.documas.eu", root: "#collection", label: "#name", value: "#organisation-code" }
collection: { url: "http://example.co,/service", format: "xml", label: "#name", value: "#id" }

root, label and value are xpath-like selectors.

The url source would work for a JSON document such as:

{
  collection: [ { name: "ABC Corp", organisation-code: "1001PTO" }, ... ],
  count: 102
}

SQL source

collection: { sql: "SELECT name,id FROM people", label: '#name', value: '#id' }

Does the SQL need to have values injected?


{
id: "update-person-assigned-to-study",
inherit: "add-person-to-study",
fields: [ { key: 'id'
}
{
id: "deassign-person-from-study",
fields: [
  { key: "study_id" },
  { key: "person_id" }
  ]
}

forms

id: "assign-person-to-study-form",
command: "assign-person-to-study",
ui: [
    { key: "names", label: "Names", type: "panel", content: [{ field: "first_name" }, { field: "last_name" }] },
    { key: "contact_details", type: "panel", content: [{ field: "email_address} }, { field: "phone_number" }] }
    { key: "save-button", type: "button", submit: true, label: "Save" }
]

content could contain any kind of UI component, not just fields. This allows us to build any kind of UI, not just plain forms.

The above schemas would be fetchable by the UI as JSON and the HTML built client-side or the server could return HTML.

maybe one form can inherit from another:

{
id: "update-person-assigned-to-study-form",
inherit: "assign-person-to-study-form",
}
{
  id: "deassign-person-from-study",
  ui: [
    { type: "field", key: "study_id", type: "hidden" },
    { type: "field", key: "person_id", type: "hidden },
    { type: "button", label: "Delete" }
  ]
 }

This would render as a button. How to put it at the side of a table row?

It might be better to not make this form specific but UI, so:

id: "assign-person-to-study-form",

ui: [
form: { 
  command: "assign-person-to-study",
  fields: [
    { key: "names", label: "Names", type: "panel", content: [{ field: "first_name" }, { field: "last_name" }] },
    { key: "contact_details", type: "panel", content: [{ field: "email_address} }, { field: "phone_number" }] }
    { key: "save-button", type: "button", submit: true, label: "Save" }
   ]
]

This will allow us to use the same system to build any UI, not just forms.

Also one UI config can be embedable in another:

ui: [
  tabs: [
    { name: '...', content: [:assign-person-to-study-form] },
    { ... }
  ]
]