Asynchronous rendering
If your perspective has several steps of computation or data fetching, you can configure it to refresh the view automatically with partial results.
To enable automatic view updates in your perspective:
-
Define an
initialData
function that returns the initial state of your data. -
Define an
enrich
function with the processing logic of the perspective. Assign computed data to thecomputedData
input property. -
Use the
computedData
property in yourrender
function to render your perspective.
const { DataRecord, DataModelEntity, Relation } = sirenapi;
const { EuiText, EuiTextColor, EuiIcon } = Eui;
const loading = 'Loading...';
function getInitialData() {
return ({
companyName: loading,
raisedAmount: loading
})
}
/**
* Common function for data fetching and calculations.
*
* While the "enrich" operation is in progress, "render" is called
* periodically to render the data with its most recent state.
*
* The 'input' object has the following properties:
* - "source" is the raw source object representing the record data
*
* - "record" is a "DataRecord" instance for the displayed record
*
* - "dataModelEntity" is a "DataModelEntity" instance for the data model
* entity that prompted the record display
*
* - "computedData" is the object used to store fetched data and make it
* available to the "render" function
*
* - "cancelPromise" is a promise that gets rejected in case of
* cancellation (for example, the user changes page or closes the
* Record Viewer)
*/
async function enrichRecordData({ source, record, dataModelEntity, computedData }) {
// It's best to bail out early if the script is used with an
// unsupported data model entity.
const rootEntity = await dataModelEntity.getRoot();
if (await rootEntity.getLabel() !== 'Companies') {
throw new Error('This script only works for Company records');
}
// We can ask for a label straight away with the getLabel() method.
const companyName = await record.getLabel();
// Store the fetched data using the "computedData" object. An internal
// interval will be using updated computed data to refresh the view.
computedData.companyName = companyName;
// Raw field values are immediately available in the "source" parameter.
computedData.location = source.countrycode ?
`${source.city} (${source.countrycode})` : source.city;
// We can fetch linked records using the "getLinkedRecords()" method of
// the "Record" instance. We should always supply the maximum number of
// records to return, plus the sorting order for the returned records.
const securedInvestmentsRelation =
(await sirenapi.Relation.getRelationsByLabel('secured'))[0];
const investments = await record.getLinkedRecords(
securedInvestmentsRelation,
{
size: 1000,
orderBy: { field: 'raised_amount', order: 'desc' }
}
);
// Now, given our investments we can calculate the total raised amount.
computedData.raisedAmount = 0;
for (const investment of investments) {
const amount =
(await investment.getFirstRawFieldValue('raised_amount')) || 0;
computedData.raisedAmount += amount;
}
}
function valueString(value) {
switch (value) {
case undefined: return <i>{loading}</i>;
case null: return <i>No data</i>;
default: return value;
}
}
function currencyString(value) {
switch (value) {
case undefined: return <i>{loading}</i>;
case null: return <i>No data</i>;
case 0: return <i>No investments</i>;
default: return `${value} $`;
}
}
/**
* A "view" perspective rendering function.
*
* Receives an "input" with "computedData" retrieved by the "enrich"
* function.
*
* Returns the rendered view as a React node.
*/
function reactView({ computedData }) {
return (
<EuiText>
<h1>
<EuiTextColor color="success">
<EuiIcon type="cheer" />
{valueString(computedData.companyName)}
</EuiTextColor>
</h1>
<p>Location: {valueString(computedData.location)}</p>
<p>Total raised amount: {currencyString(computedData.raisedAmount)}</p>
</EuiText>
);
}
/**
* A "binary" perspective rendering function.
*
* Like the "view" perspectives, the "binary" perspectives receive an input
* object with the "computedData" that prompted the download.
*
* Binary perspectives return an object representing the static resource.
*/
async function buildJsonDownload({ computedData }) {
const json = JSON.stringify(computedData, null, 2);
return { filename: 'data.json', content: json };
}
/**
* "registerPerspectives()" is called to register the "perspectives".
*
* The "view" property declares a list of registered "view" perspectives
* which will be used to display the record in the user interface.
*
* The "binary" property declares a list of registered "binary"
* perspectives which will be used to download the record representation.
*
* "initalData" is an optional property used to set the initial data prior
* to any data fetching taking place.
*
* "enrich" is an optional function used to fetch data and process it. This
* data will then be used in the "render" function.
*
* "render" is the function that will build the perspective output.
*/
context.registerPerspectives({
view: {
Tutorial: {
initialData: getInitialData,
enrich: enrichRecordData,
render: reactView
}
},
binary: {
'Json report': {
initialData: getInitialData,
enrich: enrichRecordData,
render: buildJsonDownload
}
}
});
Next steps
To make use of cached data in your script, see cached calculations.