Order entry
A form that loads one order, edits it, and saves it. Validation rules are fetched from a file the server also reads. The JSON returned to the server includes fields the screen does not handle.
This screen has no per-field handlers, no code that assembles the JSON to send, no Http, no URL, and no handling of the server's error list. It consists of three parts.
The ViewModel
Object subclass: #Vm fields: (custCode. custName. orderDate. qty. unitPrice. urgent. status).
Vm class >> new = Vm fields: {
custCode: ''. custName: ''. orderDate: (Date year: 2026 month: 1 day: 1).
qty: 0. unitPrice: 0.00. urgent: false. status: '未処理' }.
The initial values declare the types. qty: 0 makes qty an Int, unitPrice: 0.00 a Decimal, and orderDate a Date. Input from a field arrives as a string and is parsed as the field's type when stored. Storing a value of another type is a compile error.
The field table
Form class >> items = #[
Item css: 'f-code' field: 'custCode' key: 'cust_code' kind: #text.
Item css: 'f-qty' field: 'qty' key: 'qty' kind: #text.
Item css: 'f-status' field: 'status' key: 'status' kind: #choice.
Item css: 'f-urgent' field: 'urgent' key: 'urgent' kind: #flag ].
One row per field: the HTML class name, the ViewModel field name, the server's JSON key, and the kind of input control. The HTML class name and the server key are different names, and this table is where they are mapped.
kind determines where the value is read from and which event is used. #choice is a <select> with a change event; the others are <input> elements with an input event.
The screen
Screen >> typed: text at: field =
self with: { vm: (vm fieldAt: field put: (Json str: text)) }.
Which field was edited arrives in the event, so the field name is a run-time value. fieldAt:put: writes a field by name. Two handlers, one for text input and one for checkboxes, cover any number of fields.
Loading the ViewModel from the JSON document and writing it back are one line each.
Form class >> fill: vm from: doc = Form items inject: vm into: [:v :i |
(doc at: i key) isNull ifTrue: [v] ifFalse: [v fieldAt: i field put: (doc at: i key)]].
Form class >> put: vm into: doc = Form items inject: doc into: [:acc :i |
acc at: i key put: (vm fieldAt: i field)].The JSON returned to the server
put:into: writes fields into the JSON document the server sent. at:put: keeps the keys it does not replace, so fields the screen does not handle, such as id and version, go back unchanged. Numbers go back as numbers, the amount as a Decimal with its digits intact, and the flag as a boolean.
Validation
Saving validates the JSON against the shared rules. If a rule fails, its message is shown under the field and nothing is sent. If the rules pass, the JSON is sent, and the server validates it again with the same rules. If the server rejects it, the message is shown at the top of the page. See Validation for the rules and A screen with no request in it for the request.