Flexmonster Software License Agreement (“Agreement”) has been significantly revised and is effective as of September 30, 2024.
The following modifications were made:
The modified version of Flexmonster Software License Agreement is available here.
Downloading, installing, and/or continuing to use Flexmonster Software after September 30, 2024, constitutes Licensee’s acceptance of the terms and conditions of the modified version of Flexmonster Software License Agreement. If Licensee does not agree to any of these terms and conditions, they must cease using Flexmonster Software and must not download, install, use, access, or continue to access Flexmonster Software. By continuing to use Flexmonster Software or renewing the license under License Model or Maintenance after the effective date of any modifications to Agreement, Licensee accepts and agrees to be bound by the terms and conditions of the modified Agreement.
In addition to CSS themes that change the appearance of the whole component, Flexmonster provides functionality for advanced grid customization. The customizeCell() API call allows adding new styles and functionality to columns, rows, or specific cells.
The customizeCell() method allows you to set a function that will be triggered for each table cell during rendering.
Inside the function, information about the cell (e.g., the cell's position on the grid) can be accessed using the data parameter. Based on that information, you can customize the cell's representation or functionality through the cell parameter of the function. For example, it is possible to add HTML attributes, classes, inline CSS styles, and change the cell's text.
See an example that demonstrates how the function for cell customization is triggered for every table cell by painting the cell in a unique color Live example.
This guide contains several use cases of cell customization. However, they do not demonstrate all the customization possibilities. To see more examples, visit the Examples page.
Using customizeCell()
, you can alternate row colors on the grid.
In this example, we will alternate colors by adding a class to cells based on their row index (data.rowIndex). The customizeCellFunction()
can be defined in the following way:
function customizeCellFunction(cell, data) {
if (data.type == "value") {
if (data.rowIndex % 2 == 0) {
cell.addClass("alter");
} else {
return;
}
}
}
Then we need to define CSS for the class added to cells:
#fm-pivot-view .fm-grid-view div.alter {
background-color: #e1e1e1;
}
In this example, the CSS class is applied only to cells where data.type
is "value"
, which means that the cell contains numeric data.
In a similar way, you can alternate column colors based on column indexes (data.columnIndex).
Totals in rows and columns can be styled independently. The data parameter has the following properties that specify if the cell is subtotal or grand total:
Based on the values of these properties in each cell, a CSS class can be added to it. Here is an example:
function customizeCellFunction(cell, data) {
if (data.isClassicTotalRow) {
cell.addClass("fm-total-classic-r");
}
}
The CSS code sample:
#fm-pivot-view .fm-grid-view .fm-total-classic-r {
background-color: #B2DBBF;
}
The data parameter has measure, rows, and columns properties that contain info about the cell's semantics. Check out several use cases of these properties below.
You can highlight cells that contain info about a certain member regardless of the cell's position:
const highlightedMembers = {
"Country": ["Canada", "Germany"],
"Business Type": ["Warehouse"]
}
function customizeCellFunction(cell, data) {
if (data.rows) {
data.rows.forEach(row => {
if (highlightedMembers[row.hierarchyName] &&
highlightedMembers[row.hierarchyName].includes(row.caption)) {
cell.addClass("highlight");
}
});
}
if (data.columns) {
data.columns.forEach(column => {
if (highlightedMembers[column.hierarchyName] &&
highlightedMembers[column.hierarchyName].includes(column.caption)) {
cell.addClass("highlight");
}
});
}
}
After the highlight class was added, the following CSS selector can be written:
.highlight {
background-color: #FFB74D !important;
}
Cells can be highlighted differently depending on the measure they are related to.
For example, you can add information about a cell's measure to the cell's attribute:
function customizeCellFunction(cell, data) {
if (data.measure) {
cell.attr["measure"] = data.measure.name;
}
}
Now that each HTML cell will have the measure
attribute, we can add CSS selectors to highlight the rows with different colors depending on their measure:
#fm-pivot-view .fm-grid-view div[measure="Price"] {
background-color: #80CBC4 !important;
}
#fm-pivot-view .fm-grid-view div.fm-cell[measure="Quantity"] {
background-color: #FFCC80 !important;
}
#fm-pivot-view .fm-grid-view div.fm-cell[measure="Discount"] {
background-color: #81D4FA !important;
}
You can customize cells based on different semantic information. For example, add information about the cell's measure, row tuple, and column tuple to the cell's attributes:
function customizeCellFunction(cell, data) {
// Add an attribute for the cell's measure
if (data.measure) {
cell.attr.measure = data.measure.name;
}
// Add attributes for the cell's row tuple
if (data.rows) {
for (var i = 0; i < data.rows.length; i++) {
cell.attr["hierarchy-r-" + i] = data.rows[i]["hierarchyCaption"];
cell.attr["member-r-" + i] = data.rows[i]["caption"];
}
}
// Add attributes for the cell's column tuple
if (data.columns) {
for (var i = 0; i < data.columns.length; i++) {
cell.attr["hierarchy-c-" + i] = data.columns[i].hierarchyCaption;
cell.attr["member-c-" + i] = data.columns[i].caption;
}
}
}
Then you can add CSS selectors that apply different styles to cells depending on the attribute values:
#fm-pivot-view .fm-grid-view div.fm-cell[hierarchy-r-0="Country"][member-r-0="Canada"] {
background-color: #E1E1E1;
}
#fm-pivot-view .fm-grid-view div.fm-cell[measure="Price"],
#fm-pivot-view .fm-grid-view div.fm-cell.fm-header[measure="Price"] {
color: #C00000;
}
In this way, you can apply multiple styles to the cell depending on the information it contains.
The customizeCell()
method allows you to display images in cells via the cell.text property.
In this case, we replaced cell values with icons. The icons depend on the interval the value belongs to: very low (below 5), low (between 5 and 10), middle (between 10 and 25), high (between 25 and 50), very high (above 50). See the code below:
function customizeCellFunction(cell, data) {
// Customize only value cells in the compact, classic (tabular), and flat forms
if (data && data.type === "value" && !data.isDrillThrough) {
// Very low interval
if (data.value < 5) {
cell.text = `<img src="https://cdn.flexmonster.com/i/empty_pie.svg" class="centered">`;
}
// Code for other intervals
// ...
}
}
You can add links to cells via the cell.text property. For example, here's how to add links to all the countries in the report using this property:
function customizeCellFunction(cell, data) {
// Step 1. Skip a cell if it doesn't contain a country name
if (!(data.hierarchy && data.hierarchy.uniqueName == "Country")) return;
// Step 2. Create a link to the country's Wikipedia page
let newText =
`<a href='https://en.wikipedia.org/wiki/${data.escapedLabel}' target='_blank' class='link' onclick='preventExpand(event)'>${data.escapedLabel}</a>`;
// Step 3.1. Handle cells in the pivot table
if (data.type == "header" && data.member) {
let parts = cell.text.split(data.escapedLabel);
if (parts.length == 2) {
newText = parts[0] + newText + parts[1];
}
cell.text = newText;
}
// Step 3.2. Handle cells the flat table and the drill-through view
else if (data.type == "value" && (data.member || data.isDrillThrough)) {
cell.style['z-index'] = 1;
cell.text = newText;
}
}
It is possible to customize a cell based on conditional formatting rules applied to the cell. This information is contained in the data.conditions property.
The example in this section shows how to style an entire row based on the conditional formatting rule that applies to a cell in this row. The idea is the following:
<style>
tag.<style>
tag to the page with the component in the aftergriddraw event’s handler.const pivot = new Flexmonster({
// Other configs
// ...
customizeCell: customizeCellFunction,
beforegriddraw: function (e) {
if (e.smooth == false) {
shouldCleanStyles = true;
}
},
// Apply styles to the component
aftergriddraw: function (e) {
addExtraStyles();
},
});
let shouldCleanStyles = false;
let shouldAddStyles = false;
let rowsWithCondition = {};
let conditions = [];
let styleTag;
// Remove previously applied styles
function cleanExtraStyles() {
// ...
}
// Add the tag with custom CSS to the HTML page
function addExtraStyles() {
if (styleTag && shouldAddStyles) {
shouldAddStyles = false;
cleanExtraStyles();
document.head.appendChild(styleTag);
}
}
function customizeCellFunction(cell, data) {
// Additional configs
// ...
// Check if at least one conditional formatting rule applies to the cell
// and if the cell's row was not customized before
if (hasConditions(data.conditions) && !wasRowCustomized(data.rowIndex)) {
// Create an HTML tag for CSS styles
if (!styleTag) {
shouldAddStyles = true;
styleTag = document.createElement("style");
}
// Record that this row's been customized
rowsWithCondition.push(data.rowIndex);
// Get the last conditional formatting rule that applies to the cell
const conditionId = data.conditions.at(-1);
const condition = getCondition(conditionId);
if (condition == null) return;
// Specify a CSS selector for the header cell in the row
let style =
".fm-grid-layout div.fm-cell.fm-header-r[data-r='" +
data.rowIndex +
"']:not([data-c='-1']), ";
// Specify a CSS selector for other cells in the row
style +=
".fm-grid-layout div.fm-cell[data-r='" +
data.rowIndex +
"']:not([data-c='-1']):not(.fm-empty)";
// Convert the cell's conditional formatting rule to CSS and apply it to the cell's row
style +=
"{ " + condition.formatCSS.replace(/;/gi, " !important;") + " }";
// Add the created CSS to the <style> tag
styleTag.innerHTML += style;
}
}
To remove customization, call the customizeCell()
method with the null
input parameter:
pivot.customizeCell(null);