Search code examples
shinyecharts4r

echarts4r mark line with proxy chart


Is it possible to add and remove a mark line using a proxy so that the chart doesn't get fully redrawn?

To illustrate what it would look like:

library(shiny)
library(echarts4r)

df <- data.frame(
  x = 1:100,
  y = runif(100)
)

ui <- fluidPage(
  actionButton("add", "Add series"), 
  actionButton("rm", "Remove series"), 
  echarts4rOutput("chart")
)

server <- function(input, output){
  
  output$chart <- renderEcharts4r({
    e_charts(df, x) %>% 
      e_scatter(y, z)
  })
  
  # e_mark_line() - has id added for this example
  observeEvent(input$add, {
    echarts4rProxy("chart", data = df, x = x) %>%  
      e_mark_line(
        id = "my_line"
        , data = list(xAxis = 50)
        , title = "Line at 50") %>%
      e_execute()
  })
  
  # e_remove_mark_line() - is made up for this example
  observeEvent(input$rm, {
    echarts4rProxy("chart") %>%
      e_remove_mark_line("my_line")
  })
  
}
shinyApp(ui, server)

Solution

  • It's a bit odd. Apparently, a 'mark line' is attached to a specific series. I didn't add handlers for the id field, it can be done, though. However, you would also have to specify the trace it's attached to.

    BTW: in your code, you wrote e_scatter(y, z), but there is no z.

    The easiest method is to create a function like the one you eluded to in your code.

    There are two custom functions. One for Shiny in R code. One for the browser in Javascript. Combined, these create the function e_remove_markLine_p.

    The R function (specifically for Shiny applications)

    e_remove_markLine_p <- function (proxy) 
    {
      opts <- list(id = proxy$id)
      proxy$session$sendCustomMessage("e_remove_markLine_p", opts)
      return(proxy)
    }
    

    The JS function

    Shiny.addCustomMessageHandler('e_remove_markLine_p',
          function(data) {
            var chart = get_e_charts(data.id);
            let opts = chart.getOption();
            if(opts.markLine.length > 0) {
              opts.markLine.length = 0;     /* remove data */
            }
            chart.setOption(opts, true);
          })
    

    Using the power of Shiny, these two functions carry the request from the browser to R & back to the browser.

    In the code, I've changed a few other things. Instead of e_mark_line, I used e_mark_p. I'm not sure if it matters, but per the documentation, that's the appropriate function.

    Here's the entire app altogether.

    library(tidyverse)
    library(echarts4r)
    library(shiny)
    
    set.seed(315)
    df <- data.frame(x = 1:100, y = runif(100))
    
    # custom function for 'e_remove_markLine_p',
    e_remove_markLine_p <- function (proxy) 
    {
      opts <- list(id = proxy$id)
      proxy$session$sendCustomMessage("e_remove_markLine_p", opts)
      return(proxy)
    }
    
    ui <- fluidPage( 
      # adds the same call to both add and remove buttons
      tags$head(
      tags$script(HTML("
      Shiny.addCustomMessageHandler('e_remove_markLine_p',
          function(data) {
            var chart = get_e_charts(data.id);
            let opts = chart.getOption();
            if(opts.markLine.length > 0) {
              opts.markLine.length = 0;     /* remove data */
            }
            chart.setOption(opts, true);
          })
      "))),
      actionButton("add", "Add series"), 
      actionButton("rm", "Remove series"), 
      echarts4rOutput("chart")
    )
    
    server <- function(input, output){
      
      output$chart <- renderEcharts4r({
        e_charts(df, x) %>% 
          e_scatter(y)         # <--- I removed z, since it doesn't exist...
      })
      
      observeEvent(input$add, {  
        echarts4rProxy("chart", data = df, x = x) %>%  
          e_mark_p(type = "line",
                   data = list(xAxis = 50), 
                   title = "Line at 50") %>%
          e_merge() %>% e_execute() # merge when adding to the plot
      })
      
      observeEvent(input$rm, {
        echarts4rProxy("chart") %>% 
          e_remove_markLine_p()   # remove all "mark" lines
      })
      
    }
    
    shinyApp(ui, server) # show me what you got
    
    

    enter image description here

    enter image description here