In the app below there is some weird mystery going on: Why can I not use certain names for my plots?
The app was build as a dummy app to change the color of traces in plots. 1 button affects trace x in 2 plots, and there is one button for each trace.
The button names contain the name of both plots, and the trace nr they target.
When the plots are named plot1, plot2, plot3, plot4, it all works. but the actual program it is intended for uses different plot names. Changing all instances of plot1 into 'FP1plot' in the code works, changing plot3 into 'FP2plot' still works, but most names used to replace plot3 and plot 4 DO NOT work, and also using several other names to replace plot1 or plot2 with suddenly cause the plot to break and not render anymore.
Me and a more experienced javascript user who helped me develop this dummy app, are both clueless what causes this random behaviour that some names work, and others do not.
The intended names I wish to use are: 'FP1plot', 'CleanFP1', 'FP2plot', 'CleanFP2' in order.
here is the bugged shiny app with the javascript in it:
library(plotly)
library(shiny)
library(colourpicker)
library(htmlwidgets)
jscolor <- c(
"function toggleColor(id){",
" var color = document.getElementById(id).value;", # get the color of the colourpicker
" var ids = id.split('-');", # split the ids
" var plotAid = ids[2];", #get the id of plotA (FP1plot or 3)
" var plotBid = ids[3];", #get the id of plotB (plot2 or 4)
" var index = parseInt(ids[4]) -1;", #get the trace number to target
" var plotA = document.getElementById(plotAid);", #get the plot element
" var dataA = plotA.data;", #access the plot data
" var markerA = dataA[index].marker;", #access the plot's markers
" markerA.color = color;", # set the marker color
" Plotly.restyle(plotA, {marker: markerA}, [index]);", #restyle plotA
" var plotB = document.getElementById(plotBid);", # repeat steps for plot2
" var dataB = plotB.data;",
" var markerB = dataB[index].marker;",
" markerB.color = color;",
" Plotly.restyle(plotB, {marker: markerB}, [index]);",
"};"
)
colourInput2 <- function(inputId, label, value = "white",
showColour = c("both", "text", "background"),
palette = c("square", "limited"), allowedCols = NULL,
allowTransparent = FALSE, returnName = FALSE,
onchange){
input <- colourInput(inputId, label, value, showColour, palette,
allowedCols, allowTransparent, returnName)
attribs <- c(input$children[[2]]$attribs, onchange = onchange)
input$children[[2]]$attribs <- attribs
input
}
ui <- fluidPage(
tags$head(
tags$script(HTML(jscolor)) ## to add the javascript to the app
),
fluidRow(
column(4,plotlyOutput("FP1plot")),
column(4,plotlyOutput("plot2")),
column(4,uiOutput('buttons_color_1')
)
),
fluidRow(
column(4,plotlyOutput("FP2plot")),
column(4,plotlyOutput("plot4")),
column(4,uiOutput('buttons_color_2'))
)
)
server <- function(input, output, session) {
#functions to make colorinput IDs
COLElement_1 <- function(idx){sprintf("COL_button_FP1plot_plot2_%d",idx)}
COLElement_2 <- function(idx){sprintf("COL_button_FP2plot_plot4_%d",idx)}
TheColors <- c('#383838', '#5b195b','#1A237E', '#000080', '#224D17', '#cccc00', '#b37400', '#990000',
'#505050', '#a02ca0', '#000099', '#2645e0', '#099441', '#e5e500', '#cc8400', '#cc0000',
'#737373', '#e53fe5', '#0000FF', '#4479e1', '#60A830', '#ffff00','#e69500', '#ff0000',
'#b2b2b2', '#eb6ceb', '#6666ff', '#d0a3ff', '#9FDA40', '#ffff7f', '#ffa500', '#ff4c4c',
'#d9d9d9', '#f198f1', '#C5CAE9','#BBDEFB','#D9DF1D', '#ffffcc','#ffc04d', '#ff9999')
values <- reactiveValues(colors1 = TheColors, colors2 = sort(TheColors))
lapply(c(1:2), function(i) {
output[[paste('buttons_color_', i,sep = '')]] <- renderUI({
inputs <- lapply(1:3, function(x) { ## 3 in my app changes based on clustering output of my model
Idname <- if(i == 1) { COLElement_1(x) } else {COLElement_2(x) }
colour_input <- colourInput2(inputId = Idname, label = NULL,
palette = "limited", allowedCols = TheColors,
value = isolate(values[[paste('colors', i, sep = '')]][x]),
showColour = "background", returnName = FALSE,
onchange = "toggleColor(this.id)")
div(colour_input,
style = "height: 30px; width: 30px; border-radius: 6px; border-width: 2px; text-align:center; padding: 0px; display:block; margin: 10px"
)
})
do.call(tagList, inputs)
})
# useless: outputOptions(output, paste('buttons_color_', i,sep = ''), suspendWhenHidden=FALSE)
})
myplotly <- function(THEPLOT, xvar, setnr) {
markersize <- 2
markerlegendsize <- 10
colors <- isolate ({values[[paste('colors', setnr, sep = '')]] })
p <- plot_ly(source = paste('plotlyplot', THEPLOT, sep = '.'))
p <- add_trace(p, data = mtcars, x = mtcars[[xvar]], y = ~mpg, type = 'scatter', mode = 'markers', color = ~as.factor(cyl), colors = colors)
p <- layout(p, title = 'mtcars group by cyl with switching colors')
p <- plotly_build(p)
p
}
output$FP1plot <- renderPlotly({ myplotly('FP1plot', 'hp', 1) })
output$plot2 <- renderPlotly({ myplotly('plot2', 'disp', 1)})
output$FP2plot <- renderPlotly({ myplotly('FP2plot','hp', 2)})
output$plot4 <- renderPlotly({ myplotly('plot4', 'disp', 2)})
}
shinyApp(ui, server)
Updated app with workaround from S.L. but plots now render with wrong color on startup
library(plotly)
library(shiny)
library(colourpicker)
library(htmlwidgets)
jscolor <- c(
"function toggleColor(id){",
" var color = document.getElementById(id).value;", # get the color of the colourpicker
" var ids = id.split('_');", # split the id
" var plotAid = ids[2];", #get the id of plotA (plotw or 3)
" var plotBid = ids[3];", #get the id of plotB (CleanFP1 or 4)
" var index = parseInt(ids[4]) - 1;", #get the trace number to target
" var plotA = document.getElementById(plotAid);", #get the plot element
" if(plotA.innerHTML !== ''){",
" var dataA = plotA.data;", #access the plot data
" var markerA = dataA[index].marker;", #access the plot's markers
" markerA.color = color;", # set the marker color
" Plotly.restyle(plotA, {marker: markerA}, [index]);", #restyle plotA
" }",
" var plotB = document.getElementById(plotBid);", # repeat steps for CleanFP1
" if(plotB.innerHTML !== ''){",
" var dataB = plotB.data;",
" var markerB = dataB[index].marker;",
" markerB.color = color;",
" Plotly.restyle(plotB, {marker: markerB}, [index]);",
" }",
"};"
)
colourInput2 <- function(inputId, label, value = "white",
showColour = c("both", "text", "background"),
palette = c("square", "limited"), allowedCols = NULL,
allowTransparent = FALSE, returnName = FALSE,
onchange){
input <- colourInput(inputId, label, value, showColour, palette,
allowedCols, allowTransparent, returnName)
attribs <- c(input$children[[2]]$attribs, onchange = onchange)
input$children[[2]]$attribs <- attribs
input
}
ui <- fluidPage(
tags$head(
tags$script(HTML(jscolor)) ## to add the javascript to the app
),
fluidRow(
column(4,plotlyOutput("FP1plot")),
column(4,plotlyOutput("CleanFP1")),
column(4,uiOutput('buttons_color_1')
)
),
fluidRow(
column(4,plotlyOutput("FP2plot")),
column(4,plotlyOutput("CleanFP2")),
column(4,uiOutput('buttons_color_2'))
)
)
server <- function(input, output, session) {
#functions to make colorinput IDs
COLElement_1 <- function(idx){sprintf("COL_button_FP1plot_CleanFP1_%d",idx)}
COLElement_2 <- function(idx){sprintf("COL_button_FP2plot_CleanFP2_%d",idx)}
TheColors <- c('#383838', '#000080', '#b37400',
'#737373', '#e53fe5', '#0000FF', '#4479e1', '#60A830', '#ffff00','#e69500', '#ff0000',
'#b2b2b2', '#eb6ceb', '#6666ff', '#d0a3ff', '#9FDA40', '#ffff7f', '#ffa500', '#ff4c4c',
'#d9d9d9', '#f198f1', '#C5CAE9','#BBDEFB','#D9DF1D', '#ffffcc','#ffc04d', '#ff9999')
values <- reactiveValues(colors1 = TheColors, colors2 = sort(TheColors))
lapply(c(1:2), function(i) {
output[[paste('buttons_color_', i,sep = '')]] <- renderUI({
inputs <- lapply(1:3, function(x) { ## 3 in my app changes based on clustering output of my model
Idname <- if(i == 1) { COLElement_1(x) } else {COLElement_2(x) }
colour_input <- colourInput2(inputId = Idname, label = NULL,
palette = "limited", allowedCols = TheColors,
value = isolate(values[[paste('colors', i, sep = '')]][x]),
showColour = "background", returnName = FALSE,
onchange = "toggleColor(this.id)")
div(colour_input,
style = "height: 30px; width: 30px; border-radius: 6px; border-width: 2px; text-align:center; padding: 0px; display:block; margin: 10px"
)
})
do.call(tagList, inputs)
})
# useless: outputOptions(output, paste('buttons_color_', i,sep = ''), suspendWhenHidden=FALSE)
})
myplotly <- function(THEPLOT, xvar, setnr) {
markersize <- 2
markerlegendsize <- 10
colors <- isolate ({values[[paste('colors', setnr, sep = '')]] })
p <- plot_ly(source = paste('plotlyplot', THEPLOT, sep = '.'))
p <- add_trace(p, data = mtcars, x = mtcars[[xvar]], y = ~mpg, type = 'scatter', mode = 'markers', color = ~as.factor(cyl), colors = colors)
p <- layout(p, title = 'mtcars group by cyl with switching colors')
p <- plotly_build(p)
p
}
output$FP1plot <- renderPlotly({ myplotly('FP1plot', 'hp', 1) })
output$CleanFP1 <- renderPlotly({ myplotly('CleanFP1', 'disp', 1)})
output$FP2plot <- renderPlotly({ myplotly('FP2plot','hp', 2)})
output$CleanFP2 <- renderPlotly({ myplotly('CleanFP2', 'disp', 2)})
}
shinyApp(ui, server)
Supplement issues:
Plotly.restyle turns out to have the nasty habit of replacing all marker properties when you run it. In this case, we now succesfully update the color, but we loose all size and other style attributes as they are replaced by default values when Plotly.restyle runs without input for these properties.
To get a fully working javascript solution, it seems we also need to collect the size input and such to fully restyle the markers.....

