rshinyecharts4r

Render table based on datazoom in echarts4r


I have a table in a shiny app that is filtered depending on the input$brush on a graph made in ggplot. So the user brushes over on some points in the chart and gets the corresponding table below. All this is working:

  output$plot_brushed_points <- render_gt({
    dat<-dft()%>%
      select(c(4,8:16,18:19,21:38))
    dat<-dat %>% 
      mutate(across(where(is.numeric) & !(c("ODDS1","ODDS2","ODDS3")), ~ round(.x, 3)))
    res<- brushedPoints(dat,input$brush)
    gt(res) %>% 
      opt_interactive() %>% 
      tab_header(md("**In-depth parameter details**")) %>% 
          tab_options(table.width = pct(100),
                      container.width = pct(150))
  })

I'm switching all my graphs to echarts and thus I need to keep the same feature. I would like to get a table from the points filtered on the graph with. I'm assuming I need to get some coordinates from e_datazoom() but couldn't figure how to make it work. Here is the code of my graph:

output$plotui1<- renderEcharts4r({
  g1<- dft() %>%
    select(x=xvar(),y=yvar(),sz=sz(),FSCORE, HOME,AWAY) %>%
    group_by(FSCORE) %>%
    mutate(game=paste(HOME,AWAY,sep="-")) %>% 
    e_charts(x) %>%
    e_scatter(y,sz,bind = game,symbol_size = 2) %>%
    e_tooltip(formatter=htmlwidgets::JS("
           function(params){
           var vals = params.name.split('-')
          return('<strong>' + vals[0] + '-'+ vals[1] +
         '</strong><br />xvar(): ' + params.value[0] + '<br />yvar(): ' + params.value[1] +
         '<br />sz():' +params.value[2]) 
      }")) %>% 
    e_datazoom()  %>% 
    e_zoom(
      dataZoomIndex = 1
    ) %>% 
    e_legend(bottom = 0)
    g1
})

Solution

  • Here is a way.

    It is possible with JavaScript to get the percentage of the zoom start position and the percentage of the zoom end position. They are given by the datazoom event. It is also possible to get the range of the x-axis. So one can get the min x-value and the max x-value of the zoomed region. And send them to Shiny with Shiny.setInputValue.

    But when you drag a zoom handle, the datazoom event is continuously triggered. This is very fast, so I use debounce in the Shiny app to slow down the reception of the min and max values.

    library(shiny)
    library(echarts4r)
    library(htmlwidgets) # to use onRender()
    
    myChart <- USArrests |>
      e_charts(UrbanPop) |>
      e_line(Assault) |>
      e_grid() |>
      e_datazoom() |>
      onRender(
        "function(el, x) {
           var chart = this.getChart();
           chart.on('datazoom', function(e) {
             var xbounds = chart.getModel().getComponent('xAxis', 0).axis.scale.getExtent();
             var xmin = xbounds[0];
             var xmax = xbounds[1];
             var xrange = xmax - xmin;
             var min = xmin + xrange * e.start/100;
             var max = xmax - xrange * (100 - e.end)/100;
             Shiny.setInputValue('zoomBounds', [min, max]);
           });
        }")
    
    ui <- fluidPage(
      br(),
      fluidRow(
        column(
          7,
          echarts4rOutput("echart")
        ),
        column(
          5,
          tableOutput("table")
        )
      )
    )
    
    server <- function(input, output, session) {
    
      output$echart <- renderEcharts4r({
        myChart
      })
    
      zoomBounds <- reactive({
        input$zoomBounds
      }) |> debounce(1000) # use debounce to slow down
    
      zoomedData <- reactive({
        bounds <- zoomBounds()
        if(is.null(bounds)) { # initial state
          return(USArrests)
        }
        xmin <- bounds[1]
        xmax <- bounds[2]
        subset(USArrests, UrbanPop >= xmin & UrbanPop <= xmax)
      })
    
      output$table <- renderTable({
        zoomedData()
      })
    
    }
    
    
    shinyApp(ui, server)
    

    Edit: correction

    I misunderstood a point. This is easier actually, as there's no need of the zoom percentage positions. So use this JS code instead:

      onRender(
        "function(el, x) {
           var chart = this.getChart();
           chart.on('datazoom', function(e) {
             var xbounds = chart.getModel().getComponent('xAxis', 0).axis.scale.getExtent();
             Shiny.setInputValue('zoomBounds', xbounds);
           });
        }")
    

    enter image description here