Developing for Splunk Enterprise

How to create custom row expansion in a table?

astatrial
Contributor

Hello guys !
I'm trying to create a custom row expansion, which seemed to be simple task and became a big headache.
Before i even will try to add a meaningful data, i am just trying to add generic data to the rows.

This is my JS :

require([
    'splunkjs/mvc/tableview',
    'splunkjs/mvc/chartview',
    'splunkjs/mvc/searchmanager',
    'splunkjs/mvc',
    'underscore',
    'splunkjs/mvc/simplexml/ready!'],function(
    TableView,
    ChartView,
    SearchManager,
    mvc,
    _
    ){
    // Create a search manager
    var mysearch = new SearchManager({
        id: "main-search",
        preview: false,
        cache: false,
        search: "| makeresults | eval check=3 | fields - _time"
    });

    // Create a table
    var myTableView = new TableView({
        id: "mytable",
        managerid: "main-search",
        drilldown: "none",
        el: $("#mytable")
    }); 

    //create row renderer
    var EventSearchBasedRowExpansionRenderer = TableView.BaseRowExpansionRenderer.extend({
        canRender: function(rowData) {
            console.log("RowData: ", rowData);
            return true;
        },
        render: function($container, rowData) {
            // Print the rowData object to the console
            console.log("RowData: ", rowData);
            $container.append(mysearch);
        }
    });

    // Create an instance of the basic row renderer
    var tableRowRender = new EventSearchBasedRowExpansionRenderer();

    // Add the row renderer to the table
    myTableView.addRowExpansionRenderer(tableRowRender);

    // Render the table
    myTableView.render();
        });
});

In addition i added to the source of the dashboard the script="custom_table_row_expansion.js" and the table id="myTableView".

Can anyone help me with that ?

Thanks.

alt text

Labels (1)
0 Karma
1 Solution

vnravikumar
Champion

Hi @astatrial

Try this sample

<form script="custom.js">
  <label>custom_row</label>
  <fieldset submitButton="false">
    <input type="time" token="timepicker">
      <label></label>
      <default>
        <earliest></earliest>
        <latest></latest>
      </default>
    </input>
  </fieldset>
  <row>
    <panel>
      <table id="expand_table_row">
        <search>
          <query>index=_* type="threat" severity="medium" | table host source sourcetype search_id</query>
          <earliest>$timepicker.earliest$</earliest>
          <latest>$timepicker.latest$</latest>
        </search>
        <option name="drilldown">none</option>
      </table>
    </panel>
  </row>
</form>

custom.js

require([
    'splunkjs/mvc/tableview',
    'splunkjs/mvc/eventsviewerview',
    "splunkjs/mvc/chartview",
    'splunkjs/mvc/searchmanager',
    'splunkjs/mvc',
    'underscore',
    'splunkjs/mvc/simplexml/ready!'
],function(
    TableView,
    EventsViewer,
    ChartView,
    SearchManager,
    mvc,
    _
){
var EventSearchBasedRowExpansionRenderer = TableView.BaseRowExpansionRenderer.extend({
    initialize: function(args) {
        // initialize will run once, so we will set up a search and events to be reused.
        this._searchManager = new SearchManager({
            id: 'details-search-manager',
            preview: false
        });
        this.chartViewer = new ChartView({
            type: "bar",
            managerid: 'details-search-manager'
        });
    },
    canRender: function(rowData) {
        // Since more than one row expansion renderer can be registered we let each decide if
        // they can handle that data
        // Here we will always handle it.
        return true;
    },
    render: function($container, rowData) {
        // rowData contains information about the row that is expanded.  We can see the cells, fields, and values
        // We will find the sourcetype cell to use its value
        var search_idCell = _(rowData.cells).find(function (cell) {
           return cell.field === 'search_id';
        });
        console.log(search_idCell);
        //update the search with the sourcetype that we are interested in
        this._searchManager.set({
            earliest_time: "$form.timepicker.earliest$",
            latest_time: "$form.timepicker.latest$",
            search: 'index=_* type="threat" severity="medium" search_id=' + search_idCell.value +' | stats count by host'
        }, {tokens: true});
        // $container is the jquery object where we can put out content.
        // In this case we will render our chart and add it to the $container
        $container.append(this.chartViewer.render().el);
    }
});
var tableElement = mvc.Components.getInstance("expand_table_row");
tableElement.getVisualization(function(tableView) {
    // Add custom cell renderer, the table will re-render automatically.
    tableView.addRowExpansionRenderer(new EventSearchBasedRowExpansionRenderer());
});

});

alt text

View solution in original post

iamkilarunaresh
Explorer

Hello @vnravikumar I have a similar question, Please let me know if we can do it with HTML or CSS? We are Splunk cloud so it is bit hard for us with javascript.  

0 Karma

thambisetty
Super Champion

you can  not do it without using JS.

————————————
If this helps, give a like below.
0 Karma

vnravikumar
Champion

Hi @astatrial

Try this sample

<form script="custom.js">
  <label>custom_row</label>
  <fieldset submitButton="false">
    <input type="time" token="timepicker">
      <label></label>
      <default>
        <earliest></earliest>
        <latest></latest>
      </default>
    </input>
  </fieldset>
  <row>
    <panel>
      <table id="expand_table_row">
        <search>
          <query>index=_* type="threat" severity="medium" | table host source sourcetype search_id</query>
          <earliest>$timepicker.earliest$</earliest>
          <latest>$timepicker.latest$</latest>
        </search>
        <option name="drilldown">none</option>
      </table>
    </panel>
  </row>
</form>

custom.js

require([
    'splunkjs/mvc/tableview',
    'splunkjs/mvc/eventsviewerview',
    "splunkjs/mvc/chartview",
    'splunkjs/mvc/searchmanager',
    'splunkjs/mvc',
    'underscore',
    'splunkjs/mvc/simplexml/ready!'
],function(
    TableView,
    EventsViewer,
    ChartView,
    SearchManager,
    mvc,
    _
){
var EventSearchBasedRowExpansionRenderer = TableView.BaseRowExpansionRenderer.extend({
    initialize: function(args) {
        // initialize will run once, so we will set up a search and events to be reused.
        this._searchManager = new SearchManager({
            id: 'details-search-manager',
            preview: false
        });
        this.chartViewer = new ChartView({
            type: "bar",
            managerid: 'details-search-manager'
        });
    },
    canRender: function(rowData) {
        // Since more than one row expansion renderer can be registered we let each decide if
        // they can handle that data
        // Here we will always handle it.
        return true;
    },
    render: function($container, rowData) {
        // rowData contains information about the row that is expanded.  We can see the cells, fields, and values
        // We will find the sourcetype cell to use its value
        var search_idCell = _(rowData.cells).find(function (cell) {
           return cell.field === 'search_id';
        });
        console.log(search_idCell);
        //update the search with the sourcetype that we are interested in
        this._searchManager.set({
            earliest_time: "$form.timepicker.earliest$",
            latest_time: "$form.timepicker.latest$",
            search: 'index=_* type="threat" severity="medium" search_id=' + search_idCell.value +' | stats count by host'
        }, {tokens: true});
        // $container is the jquery object where we can put out content.
        // In this case we will render our chart and add it to the $container
        $container.append(this.chartViewer.render().el);
    }
});
var tableElement = mvc.Components.getInstance("expand_table_row");
tableElement.getVisualization(function(tableView) {
    // Add custom cell renderer, the table will re-render automatically.
    tableView.addRowExpansionRenderer(new EventSearchBasedRowExpansionRenderer());
});

});

alt text

astatrial
Contributor

I don't want a chart, i just want a table.
I came across this example already.

0 Karma

vnravikumar
Champion

Hi @astatrial

Try this javascript it will render a table and please let me know your requirement clearly

  require([
        'splunkjs/mvc/tableview',
        'splunkjs/mvc/eventsviewerview',
        "splunkjs/mvc/chartview",
        'splunkjs/mvc/searchmanager',
        'splunkjs/mvc',
        'underscore',
        'splunkjs/mvc/simplexml/ready!'
    ],function(
        TableView,
        EventsViewer,
        ChartView,
        SearchManager,
        mvc,
        _
    ){
    var EventSearchBasedRowExpansionRenderer = TableView.BaseRowExpansionRenderer.extend({
        initialize: function(args) {
            // initialize will run once, so we will set up a search and events to be reused.
            this._searchManager = new SearchManager({
                id: 'details-search-manager',
                preview: false
            });
            this.tableViewer = new TableView({
                pageSize: "5",
                id: "example-table",
                managerid: 'details-search-manager'
            });
        },
        canRender: function(rowData) {
            // Since more than one row expansion renderer can be registered we let each decide if
            // they can handle that data
            // Here we will always handle it.
            return true;
        },
        render: function($container, rowData) {
            // rowData contains information about the row that is expanded.  We can see the cells, fields, and values
            // We will find the sourcetype cell to use its value
            var search_idCell = _(rowData.cells).find(function (cell) {
               return cell.field === 'search_id';
            });
            console.log(search_idCell);
            //update the search with the sourcetype that we are interested in
            this._searchManager.set({
                earliest_time: "$form.timepicker.earliest$",
                latest_time: "$form.timepicker.latest$",
                search: 'index=_* type="threat" severity="medium" search_id=' + search_idCell.value +' | stats count by host'
            }, {tokens: true});
            // $container is the jquery object where we can put out content.
            // In this case we will render our chart and add it to the $container
            $container.append(this.tableViewer.render().el);
        }
    });
    var tableElement = mvc.Components.getInstance("expand_table_row");
    tableElement.getVisualization(function(tableView) {
        // Add custom cell renderer, the table will re-render automatically.
        tableView.addRowExpansionRenderer(new EventSearchBasedRowExpansionRenderer());
    });

    });
0 Karma

spkriyaz
Path Finder

Hi @vnravikumar 

I would like to know whether we can pass the entire splunk search dynamically which is used in JS for row expansion? Instead of hardcoding the search is there a way to pass the entire search dynamically to the javascript.

The problem I am facing is, i need to keep on updating the search in the JS whenever I want to add a column. So if I could pass the entire search dynamically from Splunk to the JS and the JS helps in row expansion.

Thanks,

 

Tags (1)
0 Karma

kamlesh_vaghela
SplunkTrust
SplunkTrust

@spkriyaz 

It would be great if you can share your issue details with sample code OR usecase in new question.

We are glad to help you in any JS issue

Happy Splunking 

🙂 

0 Karma

spkriyaz
Path Finder
0 Karma

spkriyaz
Path Finder

Hi @kamlesh_vaghela,

Thank you for acknowledging my question. Below is the JS which I have for table row expansion which works fine for my requirement. I want the search given in the JS should be passed from my Splunk dashboard instead of hardcoding it here. This is because I don't to update the JS whenever I want to add or delete anything in my search. 

Below search in JS which I want to be passed through Splunk dynamically instead of hardcoding in JS:

this._searchManager.set({ search: 'index=idx_omn source=vs.csv | eval key=process+" "+oprocess_start_date | search key="' + processCell.value + '" | stats values(process) as process values(oprocess_start_date) as oprocess_start_date values(oprocess_end_date) as oprocess_end_date values(otrans) as otrans values(otpm) as otpm values(oelapsed_mins) as oelapsed_mins values(total_count) as total_count values(local_currency) as local_currency values(local_swift) as local_swift values(local_amt) as local_amt values(eur_local) as eur_local BY file_number_list| table process,oprocess_start_date,oprocess_end_date,file_number_list,otrans,otpm,oelapsed_mins,total_count,local_currency,local_swift,local_amt,eur_local '});

XML:

 

<dashboard script="custom_table_row_expansion1.js">
  <label>Visa row expansion</label>
  <row>
    <panel>
      <table id="expand_with_events">
        <search>
          <query>index="idx_omn" source="vs.csv"
| eval key=process+" "+oprocess_start_date
| stats values(process) as process values(oprocess_start_date) as oprocess_start_date values(oprocess_end_date) as oprocess_end_date values(otrans) as otrans values(otpm) as otpm values(oelapsed_mins) as oelapsed_mins  sum(total_count) as total_count values(local_currency) as local_currency values(local_swift) as local_swift sum(local_amt) as local_amt sum(eur_local) as eur_local BY key
| table process oprocess_start_date oprocess_end_date otrans otpm oelapsed_mins total_count local_currency local_swift local_amt eur_local key</query>
          <earliest>0</earliest>
          <latest></latest>
          <sampleRatio>1</sampleRatio>
        </search>
        <option name="count">20</option>
        <option name="dataOverlayMode">none</option>
        <option name="drilldown">none</option>
        <option name="percentagesRow">false</option>
        <option name="refresh.display">progressbar</option>
        <option name="rowNumbers">false</option>
        <option name="totalsRow">false</option>
        <option name="wrap">true</option>
      </table>
    </panel>
  </row>
</dashboard>

 

JS:

 

require([
    'splunkjs/mvc/tableview',
    'splunkjs/mvc/chartview',
    'splunkjs/mvc/searchmanager',
    'splunkjs/mvc',
    'underscore',
    'splunkjs/mvc/simplexml/ready!'],function(
    TableView,
    ChartView,
    SearchManager,
    mvc,
    _
    ){

    var EventSearchBasedRowExpansionRenderer = TableView.BaseRowExpansionRenderer.extend({
        initialize: function(args) {
            // initialize will run once, so we will set up a search and a chart to be reused.
            this._searchManager = new SearchManager({
                id: 'details-search-manager',
                preview: false
            });
            //this._chartView = new ChartView({
            //    managerid: 'details-search-manager',
            //    'charting.legend.placement': 'none'
            //});
			this._TableView = new TableView({
                    id: 'TestTable',
                    managerid: 'details-search-manager',
                    drilldown: 'cell'
            });
        },

        canRender: function(rowData) {
            // Since more than one row expansion renderer can be registered we let each decide if they can handle that
            // data
            // Here we will always handle it.
            return true;
        },

        render: function($container, rowData) {
            // rowData contains information about the row that is expanded.  We can see the cells, fields, and values
            // We will find the key(process and start date) cell to use its value
            var processCell = _(rowData.cells).find(function (cell) {
               return cell.field === 'key';
            });
			

            //update the search with the key that we are interested in
            this._searchManager.set({ search: 'index=idx_omn source=vs.csv | eval key=process+" "+oprocess_start_date | search key="' + processCell.value + '" | stats values(process) as process values(oprocess_start_date) as oprocess_start_date values(oprocess_end_date) as oprocess_end_date values(otrans) as otrans values(otpm) as otpm values(oelapsed_mins) as oelapsed_mins values(total_count) as total_count values(local_currency) as local_currency values(local_swift) as local_swift values(local_amt) as local_amt values(eur_local) as eur_local BY file_number_list| table process,oprocess_start_date,oprocess_end_date,file_number_list,otrans,otpm,oelapsed_mins,total_count,local_currency,local_swift,local_amt,eur_local '});

            // $container is the jquery object where we can put out content.
            // In this case we will render our chart and add it to the $container
            //$container.append(this._chartView.render().el);
			$container.append(this._TableView.render().el);
        }
    });

    var tableElement = mvc.Components.getInstance("expand_with_events");
    tableElement.getVisualization(function(tableView) {
        // Add custom cell renderer, the table will re-render automatically.
        tableView.addRowExpansionRenderer(new EventSearchBasedRowExpansionRenderer());
    });
});

 

 

I did try by creating a field called "query" which has the query for the row expansion and passed it to the below JS but it didn't return any result. Please ignore this approach if there is any better solution available.

XML for passing the entire query to column called query:

 

<dashboard script="custom_table_row_expansion2.js">
  <label>Visa row expansion Clone</label>
  <row>
    <panel>
      <table id="expand_with_events">
        <search>
          <query>index="idx_omn" source="vs.csv"
| eval key=process+" "+oprocess_start_date
| eval query1 = "idx_omn source=\"visabase.csv\" | search key =\""
| eval query2 = "\"| stats values(process) as process values(oprocess_start_date) as oprocess_start_date values(oprocess_end_date) as oprocess_end_date values(otrans) as otrans values(otpm) as otpm values(oelapsed_mins) as oelapsed_mins values(total_count) as total_count values(local_currency) as local_currency values(local_swift) as local_swift values(local_amt) as local_amt values(eur_local) as eur_local BY file_number_list"
| eval query = query1+key+query2
| stats values(process) as process values(oprocess_start_date) as oprocess_start_date values(oprocess_end_date) as oprocess_end_date values(otrans) as otrans values(otpm) as otpm values(oelapsed_mins) as oelapsed_mins  sum(total_count) as total_count values(local_currency) as local_currency values(local_swift) as local_swift sum(local_amt) as local_amt sum(eur_local) as eur_local values(query) as query BY key
| table process oprocess_start_date oprocess_end_date otrans otpm oelapsed_mins total_count local_currency local_swift local_amt eur_local key query</query>
          <earliest>0</earliest>
          <latest></latest>
          <sampleRatio>1</sampleRatio>
        </search>
        <option name="count">20</option>
        <option name="dataOverlayMode">none</option>
        <option name="drilldown">none</option>
        <option name="percentagesRow">false</option>
        <option name="refresh.display">progressbar</option>
        <option name="rowNumbers">false</option>
        <option name="totalsRow">false</option>
        <option name="wrap">true</option>
      </table>
    </panel>
  </row>
</dashboard>

 

 

JS for the new try which didn't redturn any result:

 

require([
    'splunkjs/mvc/tableview',
    'splunkjs/mvc/chartview',
    'splunkjs/mvc/searchmanager',
    'splunkjs/mvc',
    'underscore',
    'splunkjs/mvc/simplexml/ready!'],function(
    TableView,
    ChartView,
    SearchManager,
    mvc,
    _
    ){

    var EventSearchBasedRowExpansionRenderer = TableView.BaseRowExpansionRenderer.extend({
        initialize: function(args) {
            // initialize will run once, so we will set up a search and a chart to be reused.
            this._searchManager = new SearchManager({
                id: 'details-search-manager',
                preview: false
            });
            //this._chartView = new ChartView({
            //    managerid: 'details-search-manager',
            //    'charting.legend.placement': 'none'
            //});
			this._TableView = new TableView({
                    id: 'TestTable',
                    managerid: 'details-search-manager',
                    drilldown: 'cell'
            });
        },

        canRender: function(rowData) {
            // Since more than one row expansion renderer can be registered we let each decide if they can handle that
            // data
            // Here we will always handle it.
            return true;
        },

        render: function($container, rowData) {
            // rowData contains information about the row that is expanded.  We can see the cells, fields, and values
            // We will find the sourcetype cell to use its value
            var processCell = _(rowData.cells).find(function (cell) {
               return cell.field === 'query';
            });
			

            //update the search with the sourcetype that we are interested in
            this._searchManager.set({ search: 'index= "' + processCell.value + '" '});

            // $container is the jquery object where we can put out content.
            // In this case we will render our chart and add it to the $container
            //$container.append(this._chartView.render().el);
			$container.append(this._TableView.render().el);
        }
    });

    var tableElement = mvc.Components.getInstance("expand_with_events");
    tableElement.getVisualization(function(tableView) {
        // Add custom cell renderer, the table will re-render automatically.
        tableView.addRowExpansionRenderer(new EventSearchBasedRowExpansionRenderer());
    });
});

 

 

 

0 Karma

kamlesh_vaghela
SplunkTrust
SplunkTrust

@spkriyaz 

Thanks for sharing it. I'll help you on this. But asked to post new question to make it separate and searchable for other Community members and for those who may be in future looking for same solution . 😜😁

 

0 Karma

hrs2019
Path Finder

@vnravikumar   can we use the same custom row expansion logic using  XML /HTML/CSS.
Since I don't have access to upload JS file in Splunk so looking for same solution in  XML/HTML/CSS?

0 Karma

astatrial
Contributor

Hi,
sorry if my question wasn't clear.
I have a dashborad and i am trying to add data to every row in a table format.
I have a base search and the panel that i want to add to the row expansion is using it.

I added the JS to the /appserver/static folder and modified the dashboard source code to add the script and the "expand_table_row".

I also tried to just create a new dashboard with the code you wrote and the add the JS, but it also didn't work .

I restarted the splunk and cleared the browser data, but i don't see any change in the dashboard (it doesn't even show the small arrow to the left of the row).

I don't know why and i can't even find a way to debug it.

When i

0 Karma

vnravikumar
Champion

Hi @astatrial

Can you inspect with your browser and check whether you are getting any javascript error. And also let me know whether you are expecting the result as like below.

alt text

0 Karma

astatrial
Contributor

This is exactly what i am trying to do(!), with no success.
I don't see any javascript errors

I added a print screen of the browser inspection .

0 Karma

vnravikumar
Champion

Whether javascript get invoked?

0 Karma

astatrial
Contributor

"Whether javascript get invoked?"
I am not sure what are you meaning here?
The splunk version is - 7.2.4.2

Also if it is not obvious i really appreciate your help!!

0 Karma

vnravikumar
Champion

I also tried in same Splunk version. Let me know whether the javascript file that you included in the dashboard, getting called. Try to put some log statement in javascript file like console.log("Testing Javascript file -----"); I guess Javascript file not called properly.

0 Karma

astatrial
Contributor

This was exactly the problem..
I edited the JS file in a wrong way and it prevented it from being called.

By the way, do you know if there is an option to add button to the row expansion
(I just want to know if you think this thing is optional or not).

Thanks for everything !

0 Karma

vnravikumar
Champion

Instead of a small triangle image do you need to add button there? If that is the case you need to handle in css.

0 Karma

astatrial
Contributor

Thanks for the response.
No, my question is if it possible that row expansion will be opened to an interactive board from which i will be able to modify things like edit lookup fields.

0 Karma
*NEW* Splunk Love Promo!
Snag a $25 Visa Gift Card for Giving Your Review!

It's another Splunk Love Special! For a limited time, you can review one of our select Splunk products through Gartner Peer Insights and receive a $25 Visa gift card!

Review:





Or Learn More in Our Blog >>