!7 srecord HCRUD_TextArea(S field, int cols, int rows) {} sclass HCRUD_Data { //LS fields() { null; } L> list() { null; } S fieldNameToHTML(S name) { ret htmlencode(humanizeLabel(name)); } S idField() { ret "id"; } Map emptyObject() { null; } // return ID O createObject(SS map) { throw unimplemented(); } // return msg S deleteObject(S id) { throw unimplemented(); } } sclass HCRUD { HCRUD_Data data; S baseLink; S newLink() { ret appendQueryToURL(baseLink, cmd := "new"); } S deleteLink(O id) { ret appendQueryToURL(baseLink, delete := id); } S render(bool withCmds) { S table = renderTable(withCmds); ret !withCmds ? table : p(ahref(newLink(), "New entry")) + table; } S encodeField(S s) { ret or(data.fieldNameToHTML(s), s); } S renderTable(bool withCmds) { L> l = data.list(); if (empty(l)) ret p("No entries"); //LS fields = data.fields(); //if (fields == null) fields = allKeysFromList_inOrder(); l = map(l, map -> { Map map2 = mapKeysAndValues( s -> encodeField(s), value -> htmlEncode2(strOrEmpty(value)), map); if (withCmds) map2.put("", ahref(deleteLink(map.get(data.idField())), "delete", onClick := "return confirm(" + jsQuote("Are you sure?") + ");")); ret map2; }); ret htmlTable2_noHtmlEncode(l); } S renderNewForm() { Map map = data.emptyObject(); ret hpostform( hhidden("action", "create") + htableRaw_valignTop( map(map, (field, value) -> ll( encodeField(field), htextfield("f_" + field, strOrEmpty(value)) )) , border := 1) + p(hsubmit("Create")), action := baseLink); } } concept Car { S brand; } p { db(); } html { new HCRUD crud; crud.baseLink = appendSlash(rawLink()); crud.data = new HCRUD_Data { //LS fields() { ret conceptFields(Car); } L> list() { ret (L) conceptsToMaps_gen(main list(Car)); } Map emptyObject() { ret mapWithoutKey(conceptToMap_gen_withNullValues(unlisted(Car)), idField()); } O createObject(SS map) { ret cnew(Car, mapToParams(map)).id; } S deleteObject(S id) { Concept c = getConcept(Car, parseLong(id)); if (c == null) ret "Object not found"; deleteConcept(c); ret "Object deleted"; } }; if (eqGet(params, "cmd", "new")) ret h1_title("New car") + crud.renderNewForm(); ret h1_title("Cars") + crud.render(true); }