Skip to content

ddcore 0.20 ​

0.20.3 — 2026-09-28 ​

Added ​

  • A site-wide ddcore range in ddcore.json. A site whose apps live in one repository declares the ddcore releases it is tested against once — "ddcore": ">=0.20.0 <0.21.0" — instead of repeating it in every defineApp. It is checked on every load with the apps' own ranges (site requires ddcore … (ddcore.json), but this binary is …), shown by ddcore doctor, and written by ddcore init; ddcore new-app in such a site gives the app no range of its own. To adopt it, move the range into ddcore.json and delete the ddcore: line from the apps that only this site ships. See "The site's range" in docs/agent/conventions.md.

Fixed ​

  • ddcore.json is written without HTML escaping, so a range saved by init or new-app reads >=0.20.0, not \u003e=0.20.0.

0.20.2 — 2026-09-28 ​

Added ​

  • Cell clicks on form grids, and frm.setRowValue (#21). A form script's grids.<table>.onCellClick.<child field>(frm, row) turns that column's read-only cells (or every cell of a gridEditMode: "dialog" grid) into buttons; the handler gets the row clicked, whatever the grid's sort or filters. frm.setRowValue(table, row | id, field | {..}, value) changes a row so the grid, the dirty state and the table's onChange follow, as an edit in the grid does. A Table's onChange now receives the row itself as a fourth argument, (frm, cdt, cdn, row), which reaches a row not saved yet (it has no id). All of it is documented in docs/agent/form-api.md.

Fixed ​

  • Editing a row in a grid's row dialog passed an undefined cdn to the table's onChange; it now passes the row's id (and the row).
  • frm.addChild returns the row as the form holds it, so a change made through the returned object reaches the grid.

0.20.1 — 2026-09-26 ​

Added ​

  • Preset row filters on form grids (#19). A Table or Report field takes gridFilters: [{ label, filters, default? }]: toggles above the grid, each showing only the rows that match its list-style filters ([["in_class", "=", 1]] or { in_class: 1 }). They run in the browser on the loaded rows, so they work on computed columns too. Toggles that are on combine with AND; default: true turns one on when the form opens. Display only, like gridSort: rows and their idx are untouched. Selection, "select all", export and a Report's totals follow the filtered rows. Labels are catalogue keys. The fields a Table's filters name, and their operators, are checked at load; tree operators are refused.

  • gridIndex: false hides a Table grid's # column (#20), for a grid sorted by something other than idx, where each row's stored position means little on screen. The column still shows by default. It can be set from extendDoctype and from frm.setDfProperty; on any field other than a Table it fails validation.

  • Sortable, exportable, selectable form grids (#18). A Table field takes gridSort: { field, order } (the default display order), gridSortable (click a column header to sort), gridExport (CSV and XLSX buttons, for users with the export permission) and gridSelect (row checkboxes, "select all" and "Delete selected"). Sorting is display only: a row's idx stays what was saved. Export sends the selected rows, or all of them, in the order on screen, with Link titles and Select labels. The properties can be set from extendDoctype and from frm.setDfProperty.

  • Computed fields (#18). computed: true declares a field with no column, never stored and always read-only, on a DocType or a child table. The controller's onLoad(doc) sets its value whenever a document is loaded for a form, a print, or the response to a save or a method. Only the computed fields survive the hook. A computed field cannot be filtered or sorted on in a list.

  • Report fieldtype (#18). { fieldtype: "Report", options: "<report name>", reportFilters: { <filter>: "<field or id>" } } shows a defineReport run for the document as a read-only grid in the form. It honours gridSort, gridSortable, gridExport and gridSelect, keeps the report's roles and permissions, and re-runs after a save, a reload or frm.refreshField(fieldname). A report name that does not exist fails the load.

  • XLSX export and column sorting on the report page. The report view exports XLSX as well as CSV, and sorts by a click on a column header.

  • ddcore.report(name, filters) in desk scripts, and frm.refreshField(fieldname).

Changed ​

  • Lists hide a hash id by default. A DocType whose id is a random hash (idGeneration: { hash: true }, or no rule) no longer shows the leading ID column in its list view (nor in the page CSV export); the title field's cell links to the document instead. A list with no other column keeps it. defineListView(..., { idColumn: true }) brings the column back.

  • onLoad in a controller now runs. It was declared in the SDK types but never called; it now fills computed fields as described above. An app that declared it only for documentation should check what it does, since any change to a field that is not computed is discarded.

  • The report page's CSV button moved from the page header to a toolbar above the table, next to the new XLSX one.

0.20.0 — 2026-09-24 ​

Added ​

  • Virtual DocTypes (DAT-07, #17). defineDoctype({ virtual: { sources: [...] } }) declares a DocType with no table whose rows are the union of other DocTypes, each source mapping its fields onto the virtual ones. Each source is read through its own permissions (role rows, permissionQuery, User Permission scopes, shares and field levels), so a user sees a row exactly when they can read the source document. A field above the user's level on that source reads as null. A row's id, and the value a Link to the virtual DocType stores, is "<Source>:<id>". Such a Link is checked against the source on save, blocks deleting a source document that is still referenced, and follows renames of the document and of the source DocType. A source_doctype field is added. All writes (insert, save, dbSet, delete, rename, import) are refused. A virtual DocType is kept out of global search unless it sets globalSearch: true, and it cannot be the allow of a User Permission. In the Desk, opening a row, a Link cell, a Link control's ↗ or a search hit sends you to the source document, and the Link picker labels each option with its source.

Changed ​

  • The desk shows a spinner while it loads. The boot screen, the workspace redirect, a form still fetching its document and a portal page used to show a bare … in the corner; they now show a centred loading spinner.

Fixed ​

  • A Table MultiSelect's list stays open after a pick. Clicking an option used to take the focus out of the input and close the list, so each value needed another click in the field. The list now stays open and offers what is left.

MIT Licensed · ddcore (Data Driven Core) · Development documentation (main)