Vlad
Vlad

Reputation: 3764

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)

Upvotes: 2

Views: 234

Answers (1)

Kat
Kat

Reputation: 18714

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

Upvotes: 1

Related Questions