rstudio / rmarkdown

Dynamic Documents for R
https://rmarkdown.rstudio.com
GNU General Public License v3.0
2.86k stars 969 forks source link

link to image in renderDataTables #316

Open vnijs opened 9 years ago

vnijs commented 9 years ago

I created an Rmd document to show student information and pictures. Using kable to generate the markdown table the images that are linked in the table cells are found.

Now I created a dynamic rmarkdown document with an actionbutton to refresh the table of students. Works great, except the path to the images is not found. I am using renderDataTables because there doesn't seem to be an option to 'renderHTML' and htmlOutput also doesn't seem to be available.

Suggestions?

jmcphers commented 9 years ago

Could you post a sample of your code? Documents rendered with runtime: shiny are rendered in a temporary folder; rmarkdown attempts to detect which additional files are needed to render, but it may miss some.

vnijs commented 9 years ago

I get names from a csv file and construct an img tag to the appropriate jpg file. See below. This link goes into a DT table with escape = FALSE. The images are not found. Even when I use absolute paths the images are not shown even though when opened in a separate tab the browser can find them. Escaping in DT seems to be working fine. I used a link a server that had the images before but would prefer to use local images. I tried adding the student-info/ directory to the document header of the rmarkdown document but it didn't have any impact. If this is not enough information you could share a working example that I could modify? Thanks @jmcphers!

 info$id <- paste0(info$first_name,".",info$last_name)
  images <- paste0("student-info/",class,"/",info$id,".jpg")
  info$images <- paste0("<img src='",images,"' title='",info$pref_name," ", info$last_name,"' style='height:60px'>")
jmcphers commented 9 years ago

This example works for me:

---
title: "Untitled"
runtime: shiny
output: html_document
---

```{r}
info <- list(id = "john")
images <- paste0("student-info/",info$id,".png")
knitr::asis_output(paste0("<img src='",images,"' title='",info$pref_name," ", info$last_name,"' style='height:60px'>"))


So--perhaps it's specific to DT tables. Could you post a larger example that uses those? 
vnijs commented 9 years ago

Looks like you are right. More realistic example below. I couldn't upload a zipped folder so please put the attached images in a directory heroins/ and when you run the document you should see that the 'regular' table displays fine but the DT table (press the Cold call button first) does not show the mugshots. Any input would be great. Thanks @jmcphers

Edit: Not sure you can access the code this way so here is gist link: https://gist.github.com/vnijs/86a9d9ac7010123f4dac


output: html_document

runtime: shiny

library(knitr)
suppressMessages(library(dplyr))
rady_class <- c("heroins")

Image source: http://www.huffingtonpost.com/2014/06/09/fairytale-mugshots_n_5475538.html

student_info <- function(rady_class) {

  info <- data.frame(first_name = c("Alice","Red"), last_name = c("Carroll","Hood"))
  info$pref_name <- info$first_name
  info$id <- paste0(info$pref_name, ".", info$last_name)
  images <- paste0(rady_class, "/", info$id, ".jpg")

  info$images <- paste0("<img src='", images, "' title='", info$pref_name," ", info$last_name,"' style='height:60px'>")
  stud_list <- kable(info, format = "markdown")

  list("stud_df" = info, "stud_list" = stud_list)
}

actionButton("cc_call_em", "Cold call")
rnd_students <- function(rady_class) {
  ret <- student_info(rady_class)$stud_df
  ret$rnd_number <- runif(nrow(ret), min = 0, max = 1)
  ret %>% arrange(desc(rnd_number)) 
}

DT::renderDataTable({
  if(input$cc_call_em == 0) return()
  rnd_students(rady_class) %>% slice(1) %>%
  DT::datatable(rownames = FALSE, style = "bootstrap", escape = FALSE,
    options = list(
      paging = FALSE, 
      searching = FALSE, 
      searchable = FALSE,
      columnDefs = list(list(className = 'dt-center', targets = "_all")),
      autoWidth = TRUE,
      processing = FALSE
    )
  )
}) 
List ``` r, echo = FALSE student_info(rady_class)$stud_list ```

alice carroll red hood

yihui commented 9 years ago

@jmcphers I have got an example from @vnijs by email. I think the problem is that resource discovery does not work for dynamically generated content. In his case, he has some images to be rendered in DataTables, but rmarkdown won't see <img/> in the HTML source because the <img/> strings are only included in JSON data. Here is a minimal self-contained example:

---
output: html_document
runtime: shiny
---

Works:

```{r}
file.copy(file.path(R.home("doc"), "html", "logo.jpg"), "logo.jpg")
d = cbind(x = '<img src="logo.jpg" title="foo bar" style="height:30px;" />', y = 'foo')
knitr::kable(d, 'markdown', escape = FALSE)

Doesn't work:

DT::datatable(d, escape = FALSE)


For shiny documents, I guess there is no need to do resource discovery _if_ it is possible to let shiny know that relative paths can be found under the directory of the Rmd document, i.e. something like `shiny::addResourcePath('/', getwd())`. I don't quite understand why we introduced the `www` convention from day one, and it is a little confusing in my eyes that a file `foo.bar` under the root directory of the app cannot be found when we use the link `foo.bar` and we have to put such files under the `www` directory instead.
vnijs commented 9 years ago

@yihui Thanks for the post. As you suggested this does not seem to be a datatable issue. renderTable({d}, sanitize.text.function = identity) cannot find the image either.

@jmcphers I can create a table that shows the image but not when I use an actionButton. Once the table is inside a renderTable, renderPrint, or datatable function the path to the images is not adjusted. I was hoping to use observeEvent to react to the action button but that doesn't seem to work (see code example below).

I created a repo at https://github.com/vnijs/coldcallr that demonstrates what I'm trying to do. cold_call_button_rmd is linked to an observeEvent and gives no response. cold_call_button_shiny is linked to renderTable and is responsive to the action button but doesn't show the images (see above).

actionButton("cold_call_button_rmd", "Cold call Rmd")

observeEvent(input$cold_call_button_rmd, {
  ## nothing shown
  kable(cold_call(sinfo, nr = 3), format = "markdown")
})
vnijs commented 9 years ago

FYI Used addResourcePath and absolute path to base directory as a workaround. See repo linked below. Please don't remove this option in rmarkdown :)

https://github.com/vnijs/coldcallr

vnijs commented 9 years ago

@yihui @jmcphers

OK. It seems using addResourcePath has stopped working in version rmarkdown version 0.8 (see repo linked below for example usage of addResourcePath to find images). Were changes made to prevent addResourcePath from working? Suggestions for workarounds/alternatives?

It still works on shinyapps.io (https://vnijs.shinyapps.io/coldcallr/coldcallr.Rmd) because I haven't updated it in a while. I am using this in class today so I had to revert to rmarkdown 0.7

https://github.com/vnijs/coldcallr