You can create you own Table view in Report and Dashboard by BellaDati Extensions feature. This tutorial describes how to create customized Table view with following specification:

  • custom look & feel (e.g. colors, padding, borders, resizing table columns, etc.)
  • displays data from existing DataSet by selected options in Filter view
  • user can select multiple table rows
  • user can invoke specific Client API endpoints and use selected table rows as parameters

Table of Contents:


Imagine situation that you have a list of PDF files somewhere on your filesystem and metadata about each PDF file stored in the DataSet, e.g.:

CountryCityDate...File Path
FranceParis2016-01-01 C:\Orders\FRA\Paris-2016-01-01.PDF
FranceParis2016-02-01 C:\Orders\FRA\Paris-2016-02-01.PDF
GermanyBerlin2016-02-22 D:\Rechnung\Berlin\Akte 22. Februar 2016.PDF
GermanyDüsseldorf2016-02-22 D:\Bestellen\Düsseldorf\Akte 22. Februar 2016.PDF

Users want to access (open/download) these files directly from BellaDati Report through their web browsers. User's second use case is to select multiple PDF files, merge them into one PDF file and download only this one PDF file. Of course, users want to filter PDF files displayed in the table by various attributes (e.g. country, city, date, etc.).


We will create Report with Filter view and Table view. Then we will create Extension that will act as customized table renderer. It will use Client API endpoints to load JSON data of existing Table view and render new table with customized CSS styles and additional features. Extension will have type "Create new page" and therefore we will embed this new page into Custom content in Report. The last step will be just hide original Table view.

You can find all source codes including prepared extension on our GitHub.


  • You have BellaDati instance in version 2.7.18+.
  • You have role Super Admin in multi-domain environment or Domain Admin in single-domain environment.
  • You are familiar with JavaScript, CSS, HTML.
  • You have set of test PDF files and metadata stored in some file or database.
  • You read our user's documentation related to extensions.

Environment Setup

  1. Enable Developer mode in your user profile. Please see section Appearance settings at Managing User Profile for more details.
  2. Prepare DataSet with data. Prepare Report with Table view and Filter view. This step is not covered by this tutorial. Please see our documentation for more details.

    You can prepare DataSet and Report very simply by importing prepared BellaApp.

  3. Prepare testing PDF files in some directory.
  4. To enable file access in your BellaDati instance and domain go to Administration and then Configuration. Please check option Enable file access. Then:
    1. For single-domain environment please set specific files paths (directory where PDF files are located) into Allowed file paths.
    2. For multi-domain environment please click Edit on Manage Domain page, go to Restrictions tab. Then check option Enable file access and set specific files paths (directory where PDF files are located) into Allowed file paths.


Please refresh extension page after each step to verify that your changes were applied correctly.

Step 1. Import and setup basic Table Renderer extension

We will use basic Table Renderer extension provided by BellaDati on GitHub. It creates a new BellaDati page with table. This table contains data from source table view that is specified as parameter. We will customize this extension to bring new features.

  1. Please download this ready–made extension from GitHub.
  2. Go to Manage extensions page that is available in the Extensions section, under Administration menu, and import this extension into your BellaDati instance.
  3. Go to your report and determine ID of source table view - Click on API button located at the top left corner of table view and see API URL value. Table view ID is a text after last "/".
  4. Set ID of table view as extension parameter called tableViewID.

At the end of this step please verify setup of extension. Go to Extension Gallery and click on name of imported extension. You should see new page with table that contains data from source table.

Step 2. Get familiar with basic Table Renderer extension

We will customize basic extension in next steps and therefore you need to get familiar with it at first.

  1. Go to Manage extensions page and click on extension name. You will be redirected to the Edit extension page.
  2. Please explore all parts of this extension. The best approach to understand how this basic extension works is play with it, and so please change prefix of CSS class names and prefix of ID attributes from "bd-" to "bd-custom-" in all parts of extension (HEAD, BODY, JavaScript and CSS). Behavior of extension will remain the same if you will do it right.
  3. Please inspect also text resource called table-renderer.jsIt contains JavaScript code that we will modify in next steps.

Step 3. Add new parameters

Our extension should be parametrized to allow users to change extension behavior without changing implemented code, therefore we need to add these parameters:

enableResizingColumnsBooleanEnable/disable resizing columns in tablefalse
hideColumnWithPathBooleanShow/hide column where file path is locatedfalse
indexOfColumnWithPathIntegerIndex of column where file path is located3
linkGetFileStringLink to our Client API endpoint that returns file from given absolute path/bi/utils/api:file?path=
linkMergePdfFilesStringLink to our Client API endpoint that merges PDF files into one PDF file/bi/utils/api:mergePdfFiles?paths=
linkViewDetailStringLink to our Client API endpoint that loads the view detail with data/bi/report/api:viewDetail/

Step 4. Create DIV containers in HTML body for controls

  1. Set following code into Content of HTML body:

    <div id="bd-custom-controls">
      <div id="bd-custom-link"></div>
      <div id="bd-custom-label"></div>
    <div id="bd-custom-table-container"></div>
  2. Add new CSS styles for new elements into text resource called table-styles.css:

    #bd-custom-controls {
      margin-bottom: 0.5em;
    #bd-custom-controls #bd-custom-link {
      display: inline;
    #bd-custom-controls #bd-custom-link .disabled {
      pointer-events: none;
      cursor: default;
      opacity: 0.6;
    #bd-custom-controls #bd-custom-link button {
      width: 160px;
      background-color: #4d94ff;
      border-color: white;
      color: white;
    #bd-custom-controls #bd-custom-label {
      display: inline;
      margin-left: 1em;

Step 5. Add checkbox as first column

  1. Update code of table-renderer.js before both loops over header columns and body columns.

    var theadCellIndex = 0;
    // insert first cell into each THEAD row
    var checkboxCell = theadRow.insertCell(theadCellIndex++); = '1em';
    // iterate over all cells in a THEAD row
    var columns = data.header[i];
    for (j = 0; j < columns.length; j++) {
    var tbodyCellIndex = 0;
    // insert first cell with checkbox into each TBODY row
    var checkboxCell = tbodyRow.insertCell(tbodyCellIndex++);
    var checkbox = document.createElement('input');
    checkbox.type = "checkbox";
    // iterate over all cells in a TBODY row
    var columns = data.body[i];
    for (j = 0; j < columns.length; j++) {

Step 6. Register events for checkboxes

  1. Add following code into table-renderer.js at the end of success function that handles response from Client API:

    // register events for rows in tbody and checkboxes
    $('.bd-custom-table tbody tr').click(function(event) {
      if ( !== 'checkbox') {
        $(':checkbox', this).trigger('click');
    $("input[type='checkbox']").change(function(e) {
      if ($(this).is(":checked")) {
      } else {
  2. Create function in table-renderer.js:

    function refreshControls() {
      // clear existing controls
      // compute count and paths    
      var count = 0;
      var paths = "";
      $('.bd-custom-table tr').filter(':has(:checkbox:checked)').find('td').filter('.bd-cell-with-link').each(function() {
        paths += paths ? (";" + :;
      // refresh link for PDF merge
      var button = document.createElement('button');
      button.type = "button";
      button.appendChild(document.createTextNode("Preview Drawing"));
      var link = document.createElement('a');
      link.href = "#PARAM=linkMergePdfFiles#" + paths;
      if (count == 0) {
        link.setAttribute('class', 'disabled');
      // refresh label with count
      var label = document.createTextNode("Selected Drawings: " + count);
      console.log(new Date().toLocaleString() + ': Selected files: ' + paths);

Step 7. Add specific functionality into column with file path

  1. At first we need to know which column should be customized, and therefore we will add this helper function into table-renderer.js:

    function isColumnWithPath(columnIndex) {
       return columnIndex == #PARAM=indexOfColumnWithPath#;
  2. Based on result from this function we will customize column title in table header:

    // set column title with appropriate header
    var textValue = column.value;
    if (isColumnWithPath(column.i)) {
       textValue += " (Link)";
  3. Based on result from this function we will customize cell content in table body:

    // fill cell with appropriate link or text
    if (isColumnWithPath(column.i)) {
      var aLink = document.createElement('a');
      aLink.href = "#PARAM=linkGetFile#" + escapedValue;
      tbodyCell.setAttribute('id', escapedValue);
      tbodyCell.setAttribute('class', 'bd-cell-with-link');
    } else {

Step 8. Add functionality to hide column with file path

  1. At first we need to know which column should be hidden and also recalculate colspans, and therefore we will add these helper functions into table-renderer.js:

    function shouldBeColumnHidden(columnIndex) {
      return isColumnWithPath(columnIndex) && #PARAM=hideColumnWithPath#;
    function computeColspanByHiddenColumn(columnIndex, colspan) {
      if (#PARAM=hideColumnWithPath#) {
        if (columnIndex <= #PARAM=indexOfColumnWithPath# && #PARAM=indexOfColumnWithPath# < (columnIndex + colspan)) {
          return colspan - 1;
        } else {
          return colspan;
      } else {
        return colspan;
  2. Based on results from these functions we will customize rendering of table header cells:

    // hide cell with link
    if (shouldBeColumnHidden(column.i)) { = 'none';
    // set colspan attributes
    if (typeof column.colspan != 'undefined') {
      theadCell.setAttribute('colspan', computeColspanByHiddenColumn(column.i, column.colspan));
  3. Based on results from these functions we will customize rendering of table body cells:

    // hide cell with link
    if (shouldBeColumnHidden(column.i)) { = 'none';
    // set colspan attributes
    if (typeof column.colspan != 'undefined') {
      tbodyCell.setAttribute('colspan', computeColspanByHiddenColumn(column.i, column.colspan));

Step 9. Make table columns resizable

There are various JavaScript and jQuery implementations that allows you to resize columns in HTML table by drag and drop. We will use jQuery plugin colResizable in this tutorial.

  1. Download minified version colResizable from GitHub and upload it into extension as a new text resource with type JavaScript.

  2. Register table in this jQuery plugin by following JavaScript code - add it into table-renderer.js at the end of success function:

    // register resizable columns - see
    if (#PARAM=enableResizingColumns#) {

Step 10. Replace source table view with extension page

  1. Go to Extension Gallery, click on name of this extension and copy URL from address bar of your web browser.
  2. Go to Report and create new Custom Content view with following code (use copied URL from previous step instead of "XXX"):

    <iframe src="XXX" width="100%" height="500" />
  3. Go to Table appearance settings of source table view and select checkbox Hide view in view mode in Options section.


You can find all source codes and also download ready-made extension from our GitHub.



  • No labels