DT icon indicating copy to clipboard operation
DT copied to clipboard

extended editor capabilities

Open LukasK13 opened this issue 7 years ago • 22 comments

I extended the editor capabilities, so distinct columns can be marked as editable and the type of the editor can be switched between a text input and a select input. The editor is enabled by using editable = TRUE option for the data tables init. The additional attributes editType and editAttribs define the type of the editor and the editor attributes. They are set up as follows:

      editType = list("1" = "text", "2" = "text", "3" = "text", "4" = "text", "5" = "select"),
      editAttribs = list("1" = list(placeholder = "Length"), "2" = list(placeholder = "Width"),
                         "3" = list(placeholder = "Length"), "4" = list(placeholder = "Width"),
                         "5" = list(options = c("setosa", "versicolor", "virginica")))
    )

The names in the list specify the target column, "text" specifies a text input and "select" specifies a select input. Currently, the only available option for a text input is the placeholder and for a select input the selectable options.

Furthermore the editor now offers reactivity to the keys enter, escape and tab.

Hopefully, this commit fixes #493

For testing is used the following code:

library(shiny)
library(DT)
shinyApp(
  ui = fluidPage(
    title = 'Double-click to edit table cells',
    titlePanel('Double-click to edit table cells'),
    DTOutput('x1')
  ),
  server = function(input, output, session) {
    d1 = iris

    output$x1 = renderDT(d1, selection = 'none', options = list(editable = T,
      editType = list("1" = "text", "2" = "text", "3" = "text", "4" = "text", "5" = "select"),
      editAttribs = list("1" = list(placeholder = "Length"), "2" = list(placeholder = "Width"),
                         "3" = list(placeholder = "Length"), "4" = list(placeholder = "Width"),
                         "5" = list(options = c("setosa", "versicolor", "virginica")))
    ))

    proxy1 = dataTableProxy('x1')

    observeEvent(input$x1_cell_edit, {
      info = input$x1_cell_edit
      str(info)
      i = info$row
      j = info$col
      v = info$value
      d1[i, j] <<- DT::coerceValue(v, d1[i, j])
      replaceData(proxy1, d1, resetPaging = FALSE)
    })
  }
)

PS: Please excuse my bad javascript, I'm both new to GitHub and javascript.

LukasK13 avatar Feb 23 '18 05:02 LukasK13

I updated my commit so the previous enabling of the editor will work. Additionally targeting the editor column is now independent from the visibility of the row names. The code for testing changed as follows:

library(shiny)
library(DT)
shinyApp(
  ui = fluidPage(
    title = 'Double-click to edit table cells',
    titlePanel('Double-click to edit table cells'),
    DTOutput('x1')
  ),
  server = function(input, output, session) {
    d1 = iris

    output$x1 = renderDT(d1, selection = 'none', editable = T, rownames = T, options = list(
      editType = list("0" = "text", "1" = "text", "2" = "text", "3" = "text", "4" = "select"),
      editAttribs = list("0" = list(placeholder = "Length"), "1" = list(placeholder = "Width"),
                         "2" = list(placeholder = "Length"), "3" = list(placeholder = "Width"),
                         "4" = list(options = c("setosa", "versicolor", "virginica")))
    ))

    proxy1 = dataTableProxy('x1')

    observeEvent(input$x1_cell_edit, {
      info = input$x1_cell_edit
      str(info)
      i = info$row
      j = info$col
      v = info$value
      d1[i, j] <<- DT::coerceValue(v, d1[i, j])
      replaceData(proxy1, d1, resetPaging = FALSE)  # important
    })
  }
)

LukasK13 avatar Feb 24 '18 01:02 LukasK13

It looks like a great patch, thanks, @LukasK13. I'll try to find time to review it tomorrow.

shrektan avatar Feb 24 '18 16:02 shrektan

Awesome. We should think about discarding changes made in the editor when the input loses focus. I think discading the changes may slow down the workflow, but would increase the security of not changing any values by accident but only by pressing tab or enter.

LukasK13 avatar Feb 24 '18 17:02 LukasK13

@LukasK13 Thanks again, I think the example's effect looks very nice, especially the dropdown box to select inputs.

As you said, in the long term, we want the workflow is smooth, effective and safe. So it's good to make this patch fits the future. Since DT is a popular package and this patch basically defines the future editing interface of DT, I need more time to think about it.

Some random thinking so far

In my limited experience, I guess the editing feature is most useful when designing an CRUD interface, as discussed here. Considering this, without thinking too much about the implementation difficulties, these features will be nice:

  • Works for both client mode and server side processing.
  • Different input mode for different field type, by default: Date -> a calendar selector, factor -> a dropdown, etc.
  • Input validations:
    • check the input types by default?
    • customised validation rules?
    • how to inform the user? pop up an error msg box?
  • Add / Delete a row easily: trigered by right-click on an row?
  • Be able to control which parts of the table is editable
  • Make the readonly / editing state explicitly: the user is either viewing the table or editing it. In the editing state, the user is aware of it clearly because the selected cell gets highlighted in a distinct color (an idea...). It's also ideal to have a button to switch the state.
  • Excel-like editing experience (my personal taste and we may learn some from rhandsontable ): The user can move around the selected cell via the arrow keys. The user can edit the cell by inputing text directly, pressing Enter or simply Mousedown.

shrektan avatar Feb 25 '18 15:02 shrektan

That sounds amazing. I think that would be the right direction to go to. Probably we should start by designing a method for initializing the editor with respect to the future additions. Then we can start by adding small features from time to time. I think using text input and select input is good for a start. Did you ever evaluate the editor by data tables?

LukasK13 avatar Feb 26 '18 03:02 LukasK13

@LukasK13 No I've never evaluated the editor of datatables but I do like its examples:

  • https://editor.datatables.net/examples/simple/simple
  • https://editor.datatables.net/examples/bubble-editing/simple
  • https://editor.datatables.net/examples/inline-editing/simple

It would be good if the editing experience here is similar to the inline-editing of the editor of datatables. What do you think?

shrektan avatar Mar 04 '18 02:03 shrektan

@shrektan The inline-editing of the editor is awesome. However I wanted to be able to select rows for deletion for example. Unfortunately it isn't possible for me to include the selection checkboxes easily in DT. That's the reason why sticked to the activation of the editor by double clicking on a cell. I would rather stick to only enable editing of available cells and leave the integration of adding and removing rows to the user.

LukasK13 avatar Mar 05 '18 16:03 LukasK13

@LukasK13 Yes, I think your current PR is good enough. However, I'm extremely busy recently. 😢 I will take a closer look as soon as I can.

shrektan avatar Mar 06 '18 13:03 shrektan

@shrektan Ok no need to hurry, I'll see if I'll find the time to implement a date picker and the automatic detection of the input type. I would recommend using a checkbox for deletion like shown in https://datatables.net/extensions/select/examples/initialisation/checkbox.html . This would enable us activating the editor on a single click and not on a double click.

LukasK13 avatar Mar 06 '18 18:03 LukasK13

Hey @shrektan I experimented with the automatic detection of the column types and was successful for text input, number input and select input. The implementation of the datetimepicker however has turned out to be problematic. I tried to use the input type datetime-local (https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/datetime-local), but this input field is not widely supported. Therefore I tried to implement a bootstrap datetime picker (https://eonasdan.github.io/bootstrap-datetimepicker/) which wasn't successful too. The implementation of a button to add a new row to the table wasn't successful either, as it will become pretty messy creating and evaluating the corresponding modal dialog. Do you think there is any option to use the data tables editor or somehow copy it (without violating the copy right)?

LukasK13 avatar Mar 11 '18 21:03 LukasK13

@LukasK13 Being busy for a while... Well, yes, the editor of datatable looks good for me but I can't see a way of using if for free... Moreover, the JS code in the trial version I downloaded gets obfuscated so we may not be able to borrow the ideas. In addition, the size scripts is about 200KB, which means it must be quite complicated to implement the full feature...

I'll see if any other way to make the editing experience closer to the official editor based on your code in the weekend.

shrektan avatar Mar 27 '18 03:03 shrektan

Hi @LukasK13 @shrektan @yihui ,

Here is the DT editor I built that has has (almost) all features of data table editor. Here is a live shiny app demo (limited active hours): https://appforiarteam.shinyapps.io/DT_Editor_Example2/ Github code: https://github.com/jienagu/DT-Editor

Let me know if this is what you all are looking for. Some features are based on Shiny such as showModal().

It supports multiple data formats (numeric, character, and date). If you want to integrate the editor features into DT package, just "parameterized" the data input part to any data (line 52-64 of server.r).

dt_editor

Welcome to discuss and let me know if you have any questions.

jienagu avatar Feb 04 '19 20:02 jienagu

Followed up with my last comment, I already parameterized the data input part using shiny modules. Repo: https://github.com/jienagu/DT_editor_shiny_module

  • This app utilize shiny module that can fit for any data.table. You just need to create your data.table and load it to the same location of your shiny app as note.rds

  • Currently, I am working on an elegant way to handle Date format so please convert any date col to character/factor before you load the data.table to this app.

Hopefully it is helpful.

jienagu avatar Apr 01 '19 23:04 jienagu

Finally I got some time to work on this and borrowed some of @LukasK13's great ideas (many thanks!). Please see my comment at https://github.com/rstudio/DT/issues/493#issuecomment-480078459.

I like the select input implemented in @LukasK13's PR, but ran out of time (still have tons of other things to do). I'll revisit it in the future. The main thing I was thinking was that the options could be automatically passed to the JS side like I did for the column filters: https://github.com/rstudio/DT/blob/0d1b8defcc1dae7db931359a89f099c9ea08fe18/R/datatables.R#L455-L458

Thanks again everyone for the very helpful input and contribution!

yihui avatar Apr 04 '19 22:04 yihui

I updated my commit so the previous enabling of the editor will work. Additionally targeting the editor column is now independent from the visibility of the row names. The code for testing changed as follows:

library(shiny)
library(DT)
shinyApp(
  ui = fluidPage(
    title = 'Double-click to edit table cells',
    titlePanel('Double-click to edit table cells'),
    DTOutput('x1')
  ),
  server = function(input, output, session) {
    d1 = iris

    output$x1 = renderDT(d1, selection = 'none', editable = T, rownames = T, options = list(
      editType = list("0" = "text", "1" = "text", "2" = "text", "3" = "text", "4" = "select"),
      editAttribs = list("0" = list(placeholder = "Length"), "1" = list(placeholder = "Width"),
                         "2" = list(placeholder = "Length"), "3" = list(placeholder = "Width"),
                         "4" = list(options = c("setosa", "versicolor", "virginica")))
    ))

    proxy1 = dataTableProxy('x1')

    observeEvent(input$x1_cell_edit, {
      info = input$x1_cell_edit
      str(info)
      i = info$row
      j = info$col
      v = info$value
      d1[i, j] <<- DT::coerceValue(v, d1[i, j])
      replaceData(proxy1, d1, resetPaging = FALSE)  # important
    })
  }
)

Has this feature implemented yet? I had retrieve the latest but wasn't able to find it. Thank you.

lsusatyo-gcmlp avatar May 31 '19 22:05 lsusatyo-gcmlp

@lsusatyo-gcmlp If you mean select inputs in the table, no. Currently only text inputs are supported.

yihui avatar May 31 '19 22:05 yihui

I am just wondering if there is a way to edit data table from a drop down list for factor variables ?

Sumeda1985 avatar Dec 16 '19 08:12 Sumeda1985

This should be possible using my fork and the code examples above. However, this PR hasn't been merged yet, as my code is pretty ugly;)

LukasK13 avatar Dec 18 '19 07:12 LukasK13

Any ETA on when this will be merged into master? @shrektan

emillykkejensen avatar Mar 30 '20 12:03 emillykkejensen

This fix sounds great! Can't wait to see it! My current drop-down workaround on the client side for DT is a real headache.

corwinjoy avatar Jun 25 '20 22:06 corwinjoy

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

CLAassistant avatar Nov 08 '23 16:11 CLAassistant

@shrektan

Excel-like editing experience (my personal taste and we may learn some from rhandsontable ): The user can move around the selected cell via the arrow keys. The user can edit the cell by inputing text directly, pressing Enter or simply Mousedown.

I did it: https://laustep.github.io/stlahblog/posts/DTcallbacks.html.

stla avatar Nov 08 '23 17:11 stla