Search code examples
rshinyplotly

R Shiny plotly::plotlyProxyInvoke Update on subplot - Updates all traces/subplots not target plot of a subplot


Good afternoon,

I'm attempting to use the plotly::plotlyProxyInvoke() function to update the data in a single plot of a subplot. I'm having issues, however, as it appears the indices argument does not apply to the correct trace in a subplot. Each plot gets updated with the same data.

Plot Before Update Original Data enter image description here

Plot After Update The second plot 'hp' was targets to have 'Camero z28' set to 0. The code is doing that, but copying the data for hp, and applying it to disp, and mpg. enter image description here

REPEX of my problem*

library(shiny)
library(tidyverse)
library(plotly)

cars <- mtcars %>% rownames_to_column(var = "carname") %>% 
  arrange(carname) %>% 
  select(carname, cyl, mpg, hp, disp) %>% 
  unique() %>% 
  mutate(cyl = as.factor(cyl)) %>% 
  pivot_longer(cols = -c(carname, cyl))

ui <- fluidPage(
  plotlyOutput("myplot", height = '600px'),
  actionButton("update", "Update Data"), 
  numericInput('newValue', "New hp Value", 0), 
  selectInput(inputId = "carName", "Car Name", multiple = FALSE, selectize = TRUE, choices = cars$carname, selected = unique(cars$carname[1]))
)

server <- function(input, output, session) {

  output$myplot <- renderPlotly({

    pp <- cars %>%
      group_by(name) %>%
      do(p =
           plot_ly(data = .) %>%
           add_bars(
             x = ~carname,
             y = ~value
             # color = ~cyl
           ) %>%
           layout(showlegend = F, yaxis = list(title = ~unique(name)))
      ) %>%
    subplot(nrows = 3, titleY = TRUE, titleX = FALSE, shareX = FALSE, shareY = FALSE)  
  
    pp

  })
   
  observeEvent(input$update, {

    cars2 <- cars %>% 
      filter(name == "hp") %>% 
      rows_update(., tibble(carname = input$carName, value = input$newValue))

    proxy <- plotlyProxy("myplot", session = shiny::getDefaultReactiveDomain())

    proxy %>% plotlyProxyInvoke("update",
                                y = list(cars2$value),
                                x = list(cars2$carname),
                                #Defining the index here is not helping? 
                                as.integer(1) #either does this. 
                                # list(list(1)) #this doesn't work
    )

  })

}


shinyApp(ui, server)

Solution

  • Please check the modified plotlyProxyInvoke call:

    library(shiny)
    library(tidyverse)
    library(plotly)
    
    cars <- mtcars %>% rownames_to_column(var = "carname") %>% 
      arrange(carname) %>% 
      select(carname, cyl, mpg, hp, disp) %>% 
      unique() %>% 
      mutate(cyl = as.factor(cyl)) %>% 
      pivot_longer(cols = -c(carname, cyl))
    
    ui <- fluidPage(
      plotlyOutput("myplot", height = '600px'),
      actionButton("update", "Update Data"), 
      numericInput('newValue', "New hp Value", 0), 
      selectInput(inputId = "carName", "Car Name", multiple = FALSE, selectize = TRUE, choices = unique(cars$carname), selected = cars$carname[1])
    )
    
    server <- function(input, output, session) {
      
      output$myplot <- renderPlotly({
        
        pp <- cars %>%
          group_by(name) %>%
          do(p =
               plot_ly(data = .) %>%
               add_bars(
                 x = ~carname,
                 y = ~value
                 # color = ~cyl
               ) %>%
               layout(showlegend = F, yaxis = list(title = ~unique(name)))
          ) %>%
          subplot(nrows = 3, titleY = TRUE, titleX = FALSE, shareX = FALSE, shareY = FALSE)  
        
        pp
        
      })
      
      observeEvent(input$update, {
        cars2 <- cars %>% 
          filter(name == "hp") %>% 
          rows_update(., tibble(carname = input$carName, value = input$newValue))
        proxy <- plotlyProxy("myplot", session = shiny::getDefaultReactiveDomain())
        proxy %>% plotlyProxyInvoke("restyle", list(y = list(cars2$value), x = list(cars2$carname)), list(0L))
        # update = restyle + relayout # subplots share a single layout
        # proxy %>% plotlyProxyInvoke("update", list(y = list(cars2$value), x = list(cars2$carname)), list(0L))
      })
      
    }
    
    
    shinyApp(ui, server)