Skip to main content

Plugins


Plugin code extensions

To enable custom logic (e.g. calculated values) the framework comes with a plugin architecture.

It is possible to include a custom Javascript file containing custom plugins to support custom implementation. For example, for certian service it might not be possible to use any of the built in transformer and a custom transformer is needed to parse the response of service. Plugin code extension provides a very neat way to achieve this with a custom Javascript file. A single plugin extension file can register different types of plugins.

Example plugin code​

const commonDateCellRenderer = params => {
const { row, column } = params;
const isValid = isDateInRange(row, column);
// dummy code
return getCellValue(isValid);
}

const formatApplicabilityStart = params => {
const { value } = params;
const match = value.match(/\[(.*?) - .*?\]/);
// dummy code
return {
value: formattedDate,
displayValue: formattedDate,
};
};

const effectivityColumnGenerator = (rows = [], columnConfig) => {
const { mergedSortedDates } = extractDatesFromField(rows, '1Month');
const columnDates = mergedSortedDates.map(date => {
return {
header: getFormattedDate(date),
field: date,
name: date,
visible: true,
cellRenderer: "commonDateCellRenderer",
width: 180
};
})
return columnDates;
}

window.plugin.registerCellRenderer("commonDateCellRenderer", commonDateCellRenderer);

window.plugin.registerCellRenderer("monthCellRenderer", monthCellRenderer);
window.plugin.registerValueFormatter('formatApplicabilityStart', formatApplicabilityStart);
window.plugin.registerValueFormatter('formatApplicabilityEnd', formatApplicabilityEnd);
window.plugin.registerValueFormatter('formatChangeDetail', formatChangeDetail);
window.plugin.registerUtility('effectivityColumnGenerator', effectivityColumnGenerator);

Transformer​

tip

We recommend using the standard built in jsonata transfomer before implementing custom plugin code. JSONata is powerful tool capable of achieveing most transformations. JSONata Documentation - JSONata

Transformers are essential tools that convert the raw data received from 3DEXPERIENCE services into a structured format that your widget's "data workspace" (Cytoscape.js) can understand and use.

Transformer is used to parse response from service and load data into graph in form of nodes and edges (see Technical overview ). Transformer Javascript function will receive the response of service, context node on which service was invoked and other configuration defined with service definition.

window.plugin.registerTransformer("nameOfTransformer", jsFunction)

Example transformer code​

const getEcosystem = transformerArgs => {
const { payload, sourceData, relationshipType, transformerInputs } = transformerArgs;
const { skip, segmentMappings } = transformerInputs;
if (skip) {
return [];
}
const elements = {};
segmentMappings.map(segmentMapping => {
const { payloadDataKey } = segmentMapping;
const dataArr = getDataFromKey(payload, payloadDataKey);
if (dataArr) {
dataArr.map(data => handleELements(elements, data, sourceData, relationshipType, segmentMapping));
} else {
handleELements(elements, payload, sourceData, relationshipType, segmentMapping);
}
});
return Object.values(elements);
};

window.plugin.registerTransformer('getEcosystem', getEcosystem);

Example using a custom transformer in configuration

"getEcosystem" : {
"uri": "/resources/enorelnav/navigate/getecosystem?tenant=OnPremise",
"transformer": {
"name": "corpusgeneric",
"inputs": {
"payloadDataKey": "newstructure.v6_structure_from",
"identifier": ["id"]
}
},
"relationType": "EngItemWhereUsed",
"method": "POST",
"payload": {
"debug": false,
"ecoSystemWithDetail": true,
"id": "{{contextId}}",
"widgetId": "ups-whereused"
},
"services": ["WhereUsedPhysicalProduct"]
},

Datahandler​

Data handlers allow you to perform logic on the data before it's passed to the cell renderer. This is useful for complex calculations or transformations that affect how a cell is displayed.

Data Handler are used to prepared data that will be displayed in table, form or charts. It is possible to implement a custom datahandler in form of Javascript function that can give flexibility to prepare data from node and edge based on some custom business logic.

Custom Datahandler can registered using below code in custom resource file.

window.plugin.registerDataHandler("nameOfDatahandler", jsFunction)

Example datahandler code​

Here we want to prepare a value - yes or no based on if none of the mentioned fields are empty

// Here we want to prepare a value - yes or no based on if none of the mentioned fields are empty
const summaryColumnHandler = ({ cell, data }) => {
const row = cell.getRow();
const eles = row.getEles();
const emptyValuesToBeChecked = [
"V_description",
"V_PartNumber",
"revision",
"Product_Family",
];
// code to check if any of the obove value is empty
if (hasSomeEmptyNoValue) {
cell.addValue("No");
return;
}
cell.addValue("Yes");
};
window.plugin.registerDataHandler("summaryColumnHandler", summaryColumnHandler);

Example using a custom datahandlerin configuration

//How summaryColumnHandler is used in table column
"summaryColumn" : {
"label": "Summary Column",
"settings": {
"datahandler": "summaryColumnHandler",
"cellRenderer": "summaryCellRenderer",
"cellRendererProps": {
"customColorMappings": [
{
"value": "Yes",
"color": "mediumseagreen"
},
{
"value": "No",
"color": "indianred"
}
]
}
},
"data": {
"datasets": [
"cad-assembly-instance"
],
"select": "SynchtoERP"
}
}

Cell Renderer​

Cell renderers are powerful tools that let you customize how data appears in each table cell.

Some table column cells required specialized view like for severity we may want to show icons or color coding to convey the value in more graphical way than plan text. This specialized view can be achieved using cell renderer. Cell renderer generates direct HTML that is displayed for the column cell. There many built in cell renderers that can be used for common needs

In addition to the built in cell renderers , custom cell renderers can be also used via plugin registration like below -

window.plugin.registerCellRenderer("Name of cell renderer", Cell Renderer JS Function);


const myCellRendererFunc = params => {
// Code to identify if cell has valid value
const backgroundColor = valid ? "background-color:grey" : "";
return `<span class="my-cell-renderer" style="${backgroundColor};display:inline-block;height:20px;width:120%;color:white;font-weight:bold;"></span>`;
}
window.plugin.registerCellRenderer("myCellRenderer", myCellRendererFunc);

Value Formatter​

Value formatters are used for simple, direct formatting of the data value itself, such as adding prefixes/suffixes, rounding numbers, or reformatting dates.

Unlike cell renderers, which generate the entire HTML for a column cell, value formatters simply modify the displayed text. For example, they can add units (like "kg" for weight) or convert dates into relative time (such as "5 days ago" instead of showing the raw date).

Custom value formatter can be used via via plugin registration like below.

window.plugin.registerValueFormatter("Name of value formatter", Value Formatter JS Function);

// Reformat the date
const reformatDate = params => {
const { value } = params;
const match = value.match(/\[(.*?) - .*?\]/);
if (!match) return { value: 'INF', displayValue: 'INF' };

const parsedDate = new Date(match[1].trim());
const formattedDate = new Intl.DateTimeFormat(locale, options).format(parsedDate);

return {
value: formattedDate,
displayValue: formattedDate,
};
};
window.plugin.registerValueFormatter('reformatDate', reformatDate);

Notifications​

Plugin code can raise the same on-screen messages the widget uses itself, through notify.

notify({ message: 'Something happened', type: 'error' });

// or the shorthand, one per type
notify.error('Upload failed');
notify.warning('Some rows were skipped');
notify.success('Saved');
notify.info('Nothing to do');

Messages stack, dismiss themselves after a few seconds, and pause while the pointer is over them. Their colours follow the notifications.colors widget setting, so a plugin message looks like every other message.

duration (milliseconds) overrides how long a message stays, and is only available on the object form — the shorthands above take a message and nothing else. duration: 0 means it stays until the user dismisses it; omit the field entirely to get the default.

notify({ message: 'Import finished with warnings', type: 'warning', duration: 0 });

Getting hold of notify​

It is handed to your function wherever that makes sense:

// Cell renderer
const myRenderer = params => {
const { row, notify } = params;
if (!row.objectId) notify.warning('Row has no id');
return row.title;
};
window.plugin.registerCellRenderer('myRenderer', myRenderer);

// Form updater utility
const myUpdater = async (edit, updateData, additionalParams) => {
const { notify, serviceCaller } = additionalParams;
try {
// ...
} catch (e) {
notify.error(e.message);
return { error: e.message };
}
};
window.plugin.registerUtility('myUpdater', myUpdater);

Everywhere else — utilities such as dynamicColumnsGenerator or dataLoadFunction, which receive their own arguments — use the global:

window.plugin.notify.error('Could not build dynamic columns');

Migrating from the old notification calls​

warning

The internal event channel used for notifications was removed in 2026.3. Code calling events.emit('notify', ...) no longer shows anything — it fails silently rather than raising an error. Search your plugin files for emit('notify' and sendNotification and update them.

BeforeAfter
params.dataUpdater.sendNotification({ message, type: 'error' })params.notify.error(message)
additionalParams.events.emit('notify', { message, type })additionalParams.notify({ message, type })
no notify in scopewindow.plugin.notify.error(message)
return { notification: { message, type } } from an updaterunchanged, still supported

notify(...) accepts the same { message, type } object the old calls took, so most changes are a rename. message may also be an array of strings, which renders one line per entry.