6

I'd like to create button (or a selectable table) in an rmarkdown file that allows a user to copy a table and paste it into word or excel with some pre-defined formatting.

Is there a way to produce:

  • A copy function that preserves table formatting, especially borders to cells
  • A copy function that captures the entirety of a table

Using this very simple dataframe:

data<-data.frame(Variable=c('Amount','Age','Happiness','Favorite Color'),
             Value=c(15,25,7,'Yellow'))

I want a user to be able to click a button in the html file such that when they paste into excel or word, they see:

enter image description here

or

enter image description here

depending on the formatting specified.

As it stands, I can make a table that has cell borders with kable,

data%>%
  kable("html",align = 'clc')%>%
  kable_styling(full_width = F,position="left",bootstrap_options = 
  c("striped","bordered"))

enter image description here This table, in the html file generated using rmarkdown, displays cell borders and has acceptable spacing and clarity (columns are wide enough to display full column headers, and the gird clearly defines cells.

When I highlight the table and paste it into Excel, I get:

enter image description here

An unsatisfactory result.

Producing a table with DT, I use:

datatable(data,extensions='Buttons',rownames=FALSE,filter=c("top"),options=list(dom='Bfrtip',buttons=c('csv','copy','excel')))

This produces a table with a CSV, Copy, and Excel button.

The result of using the Copy button and pasting into Excel is:

enter image description here

A user has data that does paste, but lacks any formatting (and puts the title from the file two rows above the datatable itself?)

Is there a way to modify the code for the DT table's Copy button to include some specified formatting, such as a missing argument that gives an option to copy some formatting to clipboard as well? Or a way to produce a standalone button that I can store an excel-formatted table behind so that a user has an way to copy my table in an excel-friendly, formatted form?

Note: I don't want to write to an excel file from R. There seem to be many options for formatting tables and writing to Excel with openxlsx and solutions here: Write from R into template in excel while preserving formatting , but this does not answer my question.

Edit: There seems to be the ability to add the formatting to the Excel button, mentioned here: https://datatables.net/extensions/buttons/examples/html5/excelBorder.html

This gives me hope that perhaps the .attr() specification of the formatting can somehow be added to the copy button.

As a first step in trying to test whether or not I can even get this intermediate step functioning, I tried:

jscode<-"function ( xlsx ){var sheet = xlsx.xl.worksheets['sheet1.xml']; $('row c[r*=10]', sheet).attr( 's', '25' );   }"

datatable(data1,class='cell-border',extensions='Buttons',rownames=FALSE,filter=c("top"),options=list(dom='Blfrtip',buttons=list(list(extend='excel',customize=jscode))))

The result is a spinning-button-of-nothingness.

When I try:

library(jsonlite)
datatable(data1,class='cell-border',extensions='Buttons',rownames=FALSE,filter=c("top"),options=list(dom='Blfrtip',buttons=list(list(extend='excel',customize=fromJSON(jscode)))))

I receive an error:

enter image description here

Since the solution to this question seems to depend on some javascript (at least if the answer will be based on datatable, I've added the javascript tag.

Pake
  • 968
  • 9
  • 24
  • It seems as though some customization can be done to `datatable`, but I don't know how to apply this to my r code, or if this helps in developing a means to apply the desired formatting to the copy button from `datatable`: https://datatables.net/extensions/buttons/examples/html5/excelBorder.html – Pake Mar 26 '21 at 19:37
  • 3
    As I've stated earlier in the comment of my [answer](https://stackoverflow.com/a/66971025/13590492), Copy won't preserve any formatting. These are some issues related to your questions: https://github.com/rstudio/DT/issues/449 https://github.com/rstudio/DT/issues/445 The questions were issued a "won'tfix" tags because Copy is meant to only preserve texts. It will only use the raw data instead of the HTML nodes. You may have to rely on manual work after you Copy. Sorry. – AOE_player Apr 06 '21 at 16:04
  • Good find. I'm wondering if there my be a creative solution where we might use something from one of the excel-focused R packages and some generic htmlwidget for a button that could copy that table to the clipboard. Here, https://cran.r-project.org/web/packages/basictabler/vignettes/v07-excelexport.html, among other places, I find Excel formatting done in R, but no luck in the avenue of making a button that'll just copy the formatted table to the clipboard as opposed to the reading/writing of excel files. – Pake Apr 06 '21 at 17:00
  • I saw this question, which introduced the BIFF format to me: https://stackoverflow.com/questions/6299824/include-formatting-information-in-windows-clipboard-copy-buffer-for-pasting-into. Maybe this'll help – Pake Apr 06 '21 at 19:46

3 Answers3

1

Library kableExtra will help you. Try this:

library(kableExtra)
data %>%
  kbl() %>%
  kable_paper("hover", full_width = F)

There are other styles, please search Rdocumentation. The table looks like this:

enter image description here

Then I copy:

enter image description here

Finally, paste in Excel:

enter image description here

Some times you need to paste as "Unicode Text" or "Text" (Can do it with special paste). enter image description here

Marcos Pérez
  • 1,260
  • 2
  • 7
  • I edited my answer. Maybe what I added can help you. – Marcos Pérez Mar 31 '21 at 21:37
  • The table pastes, but it doesn't seem like any formatting (cell borders, bold or colored text, etc.) is pasted. I've added another picture to hopefully improve clarity of the question. – Pake Apr 01 '21 at 13:38
1

This code should give you the whole table when copied and removes the table title (without any of the formatting though, maybe somebody can extend this answer):

library(DT)
DT::datatable(mtcars,
              filter = 'top', 
              class = 'cell-border stripe',
              extensions = 'Buttons',
              options = list(scrollY = 600,
                             scrollX = TRUE,
                             dom = 'lBfrtip',
                             lengthMenu=  list(c(10, 25, 100, -1), 
                                               c('10', '25', '100','All')),
                             buttons = list(
                               list(extend = "copy", text = "copy", 
                                    title= "",
                                    exportOptions = list(
                                    modifier = list(page = "all")
                                  )
                               )
                             ),
                             scrollCollapse= TRUE,
                             lengthChange = TRUE, 
                             widthChange= TRUE,
                             format = list(
                               header = TRUE
                             )))

#If you don't want rownames then you can have:
#DT::datatable(mtcars,
#              filter = 'top', 
#              class = 'cell-border stripe',
#              extensions = 'Buttons',
#              rownames = FALSE,
#              extensions = 'Buttons',
#              options = list(
#              ......)))

When you hit "Copy" and paste it in the excel you should see the following- enter image description here

Further options related to DT can be found here: https://datatables.net/reference/option/

AOE_player
  • 536
  • 2
  • 11
  • It seems as though, when pasted into excel, there is no formatting of cells, no bold headers, or any other formatting. Your link is a good resource for datatables features, but I can't figure out how you can specify formatting to the copied data on the clipboard? Can you edit your code so as to make your 'copy' button include some formatting? – Pake Apr 06 '21 at 15:27
  • 4
    I too have a limited knowledge with this package, but from however many hours I have worked with DT, I can def. say that there are limitations to the package. `Copy` just gets all the texts row by row and delaminates them with tabs (hence why you don't see borders if you paste in Word or Excel). I don't think `Copy` will get you any of the formatting you require. You will have to do manual work eventually with Copy. This solution at least lessen that manual work...sorry friend, couldn't help :( – AOE_player Apr 06 '21 at 15:43
  • 1
    Though this answer doesn't answer the question, it is the best answer here at present and the comments on this and the original question were helpful in determining that a new direction may be needed. Awarding the bounty to this answer because of that! The dialogue is much appreciated. – Pake Apr 07 '21 at 13:08
  • 1
    Thank you @Pake for your comments and bounty. Hope everything works out for you in near future :) Good luck friend. – AOE_player Apr 07 '21 at 13:39
1

I found a working solution using javascript in the rmarkdown file, and the kableExtra package for table creation.

In producing the table with kableExtra, you use table.attr to add an ID to the html table that will ultimately be produced like this:

data%>%
  kable("html",align = 'clc',table.attr="id=tableofdata")%>%
  kable_styling(full_width = F,position="left",bootstrap_options = c("striped","bordered"))%>%
  add_header_above(c("Pretty Table"=2))

After the {r} code block, paste the following script, found here: How to copy table html (data and format) to clipboard using javascript (microsoft edge browser)

<script type="text/javascript">
function selectElementContents(el) {
    let body = document.body, range, sel;
    if (document.createRange && window.getSelection) {
        range = document.createRange();
        sel = window.getSelection();
        sel.removeAllRanges();
        try {
            range.selectNodeContents(el);
            sel.addRange(range);
        } catch (e) {
            range.selectNode(el);
            sel.addRange(range);
        }
    } else if (body.createTextRange) {
        range = body.createTextRange();
        range.moveToElementText(el);
        range.select();
    }
    document.execCommand("Copy");}

</script>

This creates the copy function in javascript. Next, create a button that uses this function to copy the table and the formatting to the clipboard. See that the same term we used as the table.attr ID in the creation of our table needs to be used here within getElementByID to copy the contents.

<button type="button" onclick="selectElementContents( document.getElementById('tableofdata') );">Copy Table</button>

Note:

I found that when I knit my .Rmd file and tried using the button that appears in Rstudio's browser, clicking the button didn't do what I wanted. When I opened the resulting html file Chrome, however, clicking the button copied the table with the formatting successfully. Had I not tried chrome, I may have dismissed this as a non-working solution. I haven't tried opening with different versions of popular browsers.

Once you knit to html, the button will appear under the table, looking something like: enter image description here

After clicking the button, and pasting in Excel, the borders, bold heading, and title, show up!

enter image description here

Column width doesn't magically keep things looking good in excel, but that's a step for another day.

Pake
  • 968
  • 9
  • 24