Skip to Content
FrontendAppsReportCreating a template

Creating a template

A template is a JSON stored in templates.source. We don’t store any .tsx in the database, so a template change is just a record update, there is no build and no deploy. The reasoning is in issue #123 .


1. Which template

We have six registered templates, and the one we pick decides which data.<kind> block we have to write:

NeedTemplate nameBlockState
A table of rowsPHPReactionReport/Widget/ListingTableTemplatedata.listingRendering
A key: value blockPHPReactionReport/Widget/SummaryTemplatedata.summaryRendering
A line chartPHPReactionReport/Widget/GraphTemplatedata.graphRendering
Free HTMLPHPReactionReport/Widget/HTMLTemplatedata.htmlRendering
A meterPHPReactionReport/Widget/AuxiliaryMeterTemplatedata.meterPENDING
A gaugePHPReactionReport/Widget/GaugeTemplatedata.gaugePENDING

The names are registered in src/utils/templates/templateName.ts and the components are mapped in src/utils/templates/widgetTemplates.tsx. The <kind> of the data block has to match the template, otherwise the validation rejects it.


2. The shape

Every template has the same three parts:

{ "schemaVersion": 1, "template": { "name": "PHPReactionReport/Widget/ListingTableTemplate", "version": 1, }, "display": { "title": "Invoice Sells Report Listing", "colSpan": 12, "variant": "full", }, "data": { "listing": {}, }, }
  • template is the name, with or without the version. "name" alone resolves to the latest. The version can also be written as a suffix (…Template:2).
  • display is title, colSpan (1 to 12, where 12 is the full width) and variant (full or sidebox).
  • data is one block only, named after the kind of the template.

The validation is strict, an unknown key at any level is rejected. If the widget doesn’t render, the descriptor is the first thing to check.

The full schema, with every accepted key, is on Template JSON schema.


3. The columns

There are two ways to declare the columns, and it depends on whether we know them or not.

We don’t know them, so we take everything the DP returns:

"columns": "$data"

We know them, so we declare each one:

"columns": [ "market", { "name": "amt", "map": "$data.amount", "header": "Amount", "type": "money" } ]

A bare string is the short form of { "name": "market", "map": "$data.market" }. The map is the field of the row, header is the label displayed, and type is the format (number, money, datetime, …).

$data alone means “all the fields of the result”. $data.<field> means “this field of the current row”. It is a single level, we don’t go inside a nested object with it.


4. Samples

These are the sample files of the project. They are the fastest way to start, copy one and change the columns.

4.1 Listing, columns we know

{ "schemaVersion": 1, "template": { "name": "PHPReactionReport/Widget/ListingTableTemplate", "version": 1 }, "display": { "title": "Invoice Sells Report Listing", "colSpan": 12, "variant": "full" }, "data": { "listing": { "columns": [ { "name": "Id", "map": "$data.Id", "header": "Id", "type": "number" }, { "name": "Date", "map": "$data.Date", "header": "Date", "type": "datetime" }, { "name": "Client", "map": "$data.Client", "header": "Client" }, { "name": "nbProducts", "map": "$data.nbProducts", "header": "nbProducts", "type": "number" }, { "name": "Subtotal", "map": "$data.Subtotal", "header": "Subtotal", "type": "money" } ] } } }

4.2 Listing, nested rows

For a parent / child result, like the categories. We take all the columns and we tell the template which fields build the tree:

{ "schemaVersion": 1, "template": { "name": "PHPReactionReport/Widget/ListingTableTemplate", "version": 1 }, "display": { "title": "Categories", "colSpan": 12 }, "data": { "listing": { "columns": "$data", "nested": { "idField": "id", "parentField": "parent_id", "labelField": "name", "defaultExpanded": false } } } }

4.3 Summary

The summary ignores the columns and the headers, it displays the result of the DP as key: value rows, so the block stays empty:

{ "schemaVersion": 1, "template": { "name": "PHPReactionReport/Widget/SummaryTemplate", "version": 1 }, "display": { "title": "Invoice Sells Report Summary", "colSpan": 6, "variant": "full" }, "data": { "summary": {} } }

4.4 Graph

The first column is the X axis and every other numeric column becomes a series. columns sets the order and headers names the series:

{ "schemaVersion": 1, "template": { "name": "PHPReactionReport/Widget/GraphTemplate", "version": 1 }, "display": { "title": "Invoice Sells Report Sells Per Day", "colSpan": 12, "variant": "full" }, "data": { "graph": { "columns": ["date", "subtotal"], "headers": { "date": "Date", "subtotal": "Subtotal" }, "period": "auto", "aggregation": "sum", "fillGaps": true } } }

4.5 HTML

The HTML can be static in the descriptor, or it can come from the DP result with a sourceMap:

{ "schemaVersion": 1, "template": { "name": "PHPReactionReport/Widget/HTMLTemplate", "version": 1 }, "display": { "title": "Rendered from DP", "colSpan": 3 }, "data": { "html": { "sourceMap": "$data.html" } } }

4.6 The other samples

The rest of the files are in the repo, same folder:

FileWhat it shows
progressionListing.report.jsonA listing with a progression
auxiliaryResultsSummary.report.jsonA summary built on the auxiliary results
htmlStatic.report.jsonThe HTML written in the descriptor
kpiMeter.report.jsonThe meter block, ready for when the wiring lands

5. Testing the template

We don’t have to build a full report to see a template:

  1. /templates/{id}/render displays the template alone with the sample data of the descriptor. This is the fastest loop while we are writing it.
  2. /dashboard-widgets/{id}/render displays the widget with its real DP data and its template.
  3. /dashboard-widgets/{id}/populate displays the data only. If the widget is empty, this one tells us if the problem is the data or the template.

More about these pages in Pages & Integration.


Last updated on