stla/rAmCharts4

amCharts not always disposed when rendered together DT::datatables in dynamic UI

whakaoo opened this issue · 8 comments

I encountered some problems with dynamic UI (created using renderUI or insertUI/removeUI) and dispose of previous charts in Shiny. The values of the charts are used to fill tables created using DT::renderDataTable and therefore the use of renderUI is necessary to show contemporary dynamically created datatables and graphs.
According to my experience, it is like that previous charts are not always disposed and therefore they stop the rendering of new ones.
In fact, after a query a new UI is recreated (renderUI) or inserted (insertUI) but very often graphs (especially if the ID is the same) are not rendered, even if the DIV is closed and reopened. The same will not occur using plot_ly .
The only way (and not in all the cases) I found to overpass this limitation is to pass to renderUI the datatable and amChart as a list without using amChart4Output, but than I had other problems, i.e. reactive values linked to amCharts (e.g. input[["linecharts"]] and used as datasets in the tables were not updated.
See a short (datatable and charts are not linked here) reproducible example as follow (just try to create different UI, remove one or all of them and recreate with the same ID).
Thank you in advance for your help and suggestions,
Kind regards


library(shiny)
library(plotly)
library(rAmCharts4)
rm(list = ls(all.names = TRUE)) #will clear all objects includes hidden objects.
gc() #free up memory and report the memory usage.

ui <- fluidPage(
textInput("divID", "Enter an ID for the custom area:", ""),
helpText("Leave the text input blank for automatically unique IDs."),
actionButton("isrt", "Add a datatable"),
tags$div(id = "placeholder"),
uiOutput("prova")
)

server <- function(input, output, session) {
rv <- reactiveValues()
dat2 <- data.frame(
country = c("USA", "China", "Japan", "Germany", "UK", "France"),
visits = c(3025, 1882, 1809, 1322, 1122, 1114)
)

take a dependency on isrt button

observeEvent(input$isrt, {

# handle the case when user does not provide ID
divID <- if (input$divID == "") gsub("\\.", "", format(Sys.time(), "%H%M%OS3"))
else input$divID
dtID <- paste0(divID, "DT")
dt2ID <- paste0(divID, "DT2")
btnID <- paste0(divID, "rmv")
plotlyID <- paste0(divID, "plotly")
amChartID <- paste0(divID, "amChart")

# only create button if there is none
if (is.null(rv[[divID]])) {
  # output$prova <- renderUI({
  #   plotlist <- list(
  #   actionButton("download", "button1", class = "pull-right btn btn-danger"),
  #   actionButton("upload", "button2", class = "pull-right btn btn-danger"),
  #   actionButton("modify", "button3", class = "pull-right btn btn-danger"),
  #   actionButton(btnID, "Remove this UI", class = "pull-right btn btn-danger"),
  #   DT::dataTableOutput(dtID),
  #   DT::dataTableOutput(dt2ID),
  #   plotlyOutput(plotlyID),
  #   amChart4Output(amChartID, height = "500px"),
  #   hr()
  #   )
  #   do.call(tagList, plotlist)
  # })
  insertUI(
    selector = "#placeholder",
    ui = tags$div(id = divID,
                  actionButton("download", "button1", class = "pull-right btn btn-danger"),
                  actionButton("upload", "button2", class = "pull-right btn btn-danger"),
                  actionButton("modify", "button3", class = "pull-right btn btn-danger"),
                  actionButton(btnID, "Remove this UI", class = "pull-right btn btn-danger"),
                  DT::dataTableOutput(dtID),
                  DT::dataTableOutput(dt2ID),
                  plotlyOutput(plotlyID),
                  amChart4Output(amChartID, height = "500px"),
                  hr()
    )
  )

  output[[dtID]] <- DT::renderDataTable(head(iris))
  output[[dt2ID]] <- DT::renderDataTable(head(cars))
  output[[plotlyID]] <- renderPlotly(
    plot_ly(data = cars, x= ~speed, y = ~dist)
  )
  output[[amChartID]] <- renderAmChart4({
    amBarChart(
      data = dat2,
      data2 = dat2,
      width = "600px",
      category = "country", values = "visits",
      draggable = TRUE,
      tooltip =
        "[bold font-style:italic #ffff00]{valueY.value.formatNumber('#,###.')}[/]",
      chartTitle =
        amText(text = "Visits per country", fontSize = 22, color = "orangered"),
      xAxis = list(title = amText(text = "Country", color = "maroon")),
      yAxis = list(
        title = amText(text = "Visits", color = "maroon"),
        gridLines = amLine(color = "orange", width = 1, opacity = 0.4)
      ),
      yLimits = c(0, 4000),
      valueFormatter = "#,###.",
      caption = amText(text = "Year 2018", color = "red"),
      theme = "material")
  })
  # make a note of the ID of this section, so that it is not repeated accidentally
  rv[[divID]] <- TRUE

  # create a listener on the newly-created button that will
  # remove it from the app when clicked
  observeEvent(input[[btnID]], {
    removeUI(selector = paste0("#", divID))

    rv[[divID]] <- NULL

  }, ignoreInit = TRUE, once = TRUE)

  # otherwise, print a message to the console
} else {
  message("The button has already been created!")
}

})
}

shinyApp(ui = ui, server = server)

stla commented

Hello @whakaoo

I have not tried your code yet. Do you mean this problem occurs only when there also is a DT::datatable?

Are you using the latest CRAN version of rAmCharts4? Because I fixed a problem with the disposal in this version.

Hello,

Thank you for your fast reply.

I am currently using:

  • rAmChart4 v.1.3.1 from CRAN

  • R version 4.0.2 (2020-06-22) -- "Taking Off Again"
    Copyright (C) 2020 The R Foundation for Statistical Computing
    Platform: x86_64-w64-mingw32/x64 (64-bit)

  • RStudio (v.1.3.959),

  • Microsoft W10 laptop using Chrome as the default web browser.

My impression is that something is going wrong when renderUI (or insertUI) is used to output together AmChart4 and something else (e.g. DT::datatables or renderPrint)

Today I tried different code options but I'm still having problems.

A brief summary of the daily attempts I have tried so far:

If inside a renderUI and a lapply loop I enclose the output as a list, as for instance:

variable <- list(

output[[id_plots]] <- renderAmChart4({

        amLineChart(  etc...)

}),

output[[id_table]] <- DT::renderDataTable({

        DT::datatable( input[[id_plots]]..etc...)

}),

output[[id_tableplots]] <- renderPrint({

        input[[id_plots]]

        })

)

everything is fine, AmChart4 were disposed every time I changed the query (e.g. using a dropdown menu to select variables) or TABs but, inevitably as all the output were inside a list, I loosed the reactivity and the variable input[[id_plots]] was null and as a consequence the DT::datatables were not showed (instead if I build the DT::datatables using the iris dataset they were showed) and the renderPrint reported a NULL output.

Instead if, inside a renderUI and an lapply loop, I didn't enclose the output as a list, but I pass them after as follow:

output[[id_plots]] <- renderAmChart4({

        amLineChart(  etc...)

})

output[[id_table]] <- DT::renderDataTable({

        DT::datatable( input[[id_plots]]..etc...)

})

output[[id_tableplots]] <- renderPrint({

        input[[id_plots]]

        })

variable<- list(

          DTOutput(id_table),

          amChart4Output(id_plots)

         verbatimTextOutput(id_tableplots)

        )

I had the reactivity, and the first display of the webpage was fine but as soon as I make a new query the AmChart4 were not showed and not disposed and I had an error (reported in the F12 console) in the webpage as follows:

VM61:141 TypeError: Cannot read property 'dispose' of undefined

at AmLineChart.componentDidUpdate (<anonymous>:123653:16)

at Aj (<anonymous>:144:364)

at Pj (<anonymous>:176:361)

at unstable_runWithPriority (<anonymous>:24:26)

at Za (<anonymous>:73:8)

at eb (<anonymous>:170:163)

at ff (<anonymous>:162:317)

at <anonymous>:73:230

at unstable_runWithPriority (<anonymous>:24:26)

at Za (<anonymous>:73:8)

Xe @ VM61:141

VM61:73 Uncaught TypeError: Cannot read property 'dispose' of undefined

at AmLineChart.componentDidUpdate (<anonymous>:123653:16)

at Aj (<anonymous>:144:364)

at Pj (<anonymous>:176:361)

at unstable_runWithPriority (<anonymous>:24:26)

at Za (<anonymous>:73:8)

at eb (<anonymous>:170:163)

at ff (<anonymous>:162:317)

at <anonymous>:73:230

at unstable_runWithPriority (<anonymous>:24:26)

at Za (<anonymous>:73:8)

VM63:111722 Chart was not disposed id-22

warn @ VM63:111722

VM63:111722 Chart was not disposed id-5163


I also tried to export the lapply as a variable (e.g. outputs1 <- lapply...etc. ) and input to the renderUI afterwards , as follow:

  output$singleParms <- renderUI({

    do.call(tagList, outputs1)

  })

but I had the same problems as above.

Finally, I tried to attach a random number to the id_plots (e.g. with sprintf and runif(1) ) and in this case every time I make a new query or I changed the TAB the AmChart4 were rendered and displayed correctly but of course the previous ones were probably still in memory and memory leaks will probably occur in the long term

Thanks, if you can find a solution, I will really appreciate your support and help.

Kind regards,

Hi there,
any news on my issue?
Thanks

here an online example of the issue.
Try to add 2 datatables with different ID (e.g. 1 and 2), then remove both UIs and add again with the same ID.
You will see how the UI will be rendered again but without amcharts.
https://teomau69.shinyapps.io/rAmcharts4/?_ga=2.45589921.400235614.1622455727-1962902425.1621086880

Interestingly, if you insert in the DT:datatable the following code:
callback = JS(
"Shiny.addCustomMessageHandler(",
" 'selectRow',",
" function(index) {",
" table.row(index - 1).select();",
" }",
");",
and in the observeEvent of the selected row ( input$id_rows_selected) the following code:
session$sendCustomMessage("selectRow", NULL)
session$sendCustomMessage("selectRow", input$id_rows_selected)
then the previous chart is disposed and the new chart is rendered correctly.

Similar result is obtained by passing to the Chart the y-value by selecting, deselecting and re-selecting the row in the DT::datatable manually by using the mouse or through a drop-down menu

stla commented

Hello @whakaoo

I've just attempted to fix this bug. Could you give a try?

remotes::install_github("stla/rAmCharts4")

Hi @stla,
Yes, it works! Great!
Thanks a lot.
Kind regards,
Whakaoo

stla commented

Coooolll! Thanks for the report!