itables icon indicating copy to clipboard operation
itables copied to clipboard

Row selection?

Open michael-erasmus opened this issue 2 years ago • 10 comments

I'm using itables in a Shiny app and I was wondering if there's a way I can implement row selection?

michael-erasmus avatar Nov 29 '23 00:11 michael-erasmus

Oh I found this in the documentation: https://mwouts.github.io/itables/advanced_parameters.html#select-rows

So it's not currently supported? Any way I could implement it myself?

michael-erasmus avatar Nov 29 '23 00:11 michael-erasmus

Thanks for looking into this! Do you think you could add the extension to the connected template https://github.com/mwouts/itables/blob/main/itables/html/datatables_template_connected.html ? That would be a great start !

mwouts avatar Nov 29 '23 07:11 mwouts

Sorry I actually don't have the bandwidth to do a full PR, but I can document a hacky workaround I was able to figure out.

I manually added the select extension js/css files and then I inject my own document.ready code into my webpage

app_ui = ui.page_fluid(
  ui.head_content(
          ui.tags.link(rel="stylesheet", type="text/css", href="https://cdn.datatables.net/buttons/2.4.2/css/buttons.dataTables.min.css"),
          ui.tags.script(src="https://cdn.datatables.net/1.10.24/js/jquery.dataTables.min.js"),
          ui.tags.link(rel="stylesheet", type="text/css", href="https://cdn.datatables.net/select/1.7.0/css/select.dataTables.min.css"),
          ui.tags.script(src="https://cdn.datatables.net/select/1.7.0/js/dataTables.select.min.js")
        
        
        # custom js to send the selected row data to Shiny server
        ui.HTML("""
        <script>
            const attachSelectHandler = function() {
                setTimeout(function() { //hack to wait for the table to initialized
                    dt = $('table').DataTable();
                    dt.on('select', function (e, dt, type, indexes) {
                        if (type === 'row' && indexes.length > 0) {
                            // Send the selected row data to Shiny server
                            var index = indexes.pop();
                            console.log('selected index', index);
                            Shiny.setInputValue('selectedRow', index);
                        }
                    });
                },2000);
            };
            $(document).ready(function () {
                attachSelectHandler();
                Shiny.addCustomMessageHandler("clearSelection", function(message) {
                    console.log('clearSelection');
                    Shiny.setInputValue('selectedRow', 0);
                    attachSelectHandler();
                });
            });
        </script>
        """)
        
        ...
)

Then in my server code I can make sure to initialized DT with select=True


def server(input, output, session):
     async def clear_selection(): #this can be called to clear the selection from the server
        await session.send_custom_message(type="clearSelection", message=None)

    @render.ui
    def ouput_df():
        df = get_df()
        dt_html = DT(df, select=True)
        return ui.HTML(dt_html)

I'll leave it to you if you want to keep the issue open!

michael-erasmus avatar Nov 29 '23 22:11 michael-erasmus

Thanks! Yes, please leave the issue open. Thanks for the Shiny code, I am sure it will be helpful to others until we can support extensions in itables!

mwouts avatar Dec 02 '23 09:12 mwouts

Row selection is now supported (well, included in the default bundle) in itables==2.0. @michael-erasmus do you think you could update your example above? Thanks!

mwouts avatar Mar 16 '24 16:03 mwouts

Thank you for the great tool. I also see the select row is now available, but how can I get the selected row id? what is the input element I can use from Shiny?

maxmoro avatar Jun 01 '24 07:06 maxmoro

Thanks @maxmoro . Well while the select extension is bundled within ITables, so rows can be selected, at the moment it's not possible yet to get this information back in Python (the code above was for an earlier version of ITables). I'll update this issue when I find out how to do this.

mwouts avatar Jun 01 '24 19:06 mwouts

Thank you @mwouts for the prompt reply. I perfectly understand. I was able to adapt the code above to get the row_id. I'm good for the moment, and I'm happy to test the updated select version as soon is out.

maxmoro avatar Jun 02 '24 20:06 maxmoro

Oh great! Can you share back how you do it? Thanks!

mwouts avatar Jun 02 '24 21:06 mwouts

Unfortunately my code it is not fully working, I didn't notice I was reloading the jquery library twice. (so multiple tables have been created) I'm now stuck at the error DataTable is not a function (I'm very new to JS) This is my snippet of the attachSelectHandler code

const attachSelectHandler = function() {
                console.log('p1')
                setTimeout(function() { //hack to wait for the table to initialized
                    var tables = $('.dataTable')
                    console.log('p2')
                    console.log(tables)
                    tables.each(function() {
                        var table = $(this).DataTable();
                        console.log('c')
                        // console.log(table)
                        table.on('select', function (e, dt, type, indexes) {
                            if (type === 'row' && indexes.length > 0) {
                                // console.log('d')
                                tableId = dt.table().node().id
                                // Send the selected row data to Shiny server
                                var index = indexes.pop();
                                // console.log(tableId)
                                console.log('selected index', index);
                                Shiny.setInputValue(`${tableId}_selectedRow`, index);
                            };
                        });
                    });
                },2000);
            };

Looking at the web, looks like the right library is not loaded. or we need a defer on jquery.dataTable.mins.js link

Or, do you have any insight?

maxmoro avatar Jun 03 '24 06:06 maxmoro

Hey @michael-erasmus , I am finally coming back to this topic!

In the PR #319 I have been able to do the following

  • Provide a Jupyter widget for ITables
  • Make DT work offline in a Shiny app (this is the call to init_shiny_mode at https://github.com/mwouts/itables/blob/try_anywidget/tests/sample_python_apps/itables_in_a_shiny_app.py)
  • Create and update a shiny input {table_id}_selected_rows when an explicit table_id is passed

There is one thing that I have not been able to do yet, that is to update the row selection using e.g.

session.send_custom_message("countries_select_rows", {"selected_rows":[0,1]})

Is that something that you are still interested in? I tried to add the following to selected_row_code:

Shiny.addCustomMessageHandler("{table_id}_select_rows", function(message) {{
                DataTable.set_selected_rows(dt, filtered_row_count, message.selected_rows);
            }});

but that was not enough. Do you think we need to solve that? Note that passing selected_rows as an argument to DT already works in that PR.

mwouts avatar Sep 21 '24 22:09 mwouts

I'll close this issue as DT now has selected_rows argument in ITables v2.2 (see this example).

Feel free to open another one re updating the row selection using a custom message!

mwouts avatar Sep 22 '24 14:09 mwouts

Actually using the ITable widget seems way easier! I have updated the documentation accordingly. Let me know what you think. Thanks

mwouts avatar Sep 29 '24 23:09 mwouts