Shiny app requires double-click for Highcharter plot updates in R

Here is my code snippet that relates to the question I asked earlier today. However, I am facing a new issue that has me stumped. After clicking the actionButton to update the chart, it seems like the chart only refreshes after the second click. Surprisingly, the print statement works just fine after the first click. Can someone help me figure out what's causing this behavior?

library(highcharter)
library(shiny)
library(shinyjs)

df <- data.frame(
    a = floor(runif(10, min = 1, max = 10)),
    b = floor(runif(10, min = 1, max = 10))
)


updaterfunction <- function(chartid, sendid, df, session) {

    message = jsonlite::toJSON(df)
    session$sendCustomMessage(sendid, message)

    jscode <- paste0('Shiny.addCustomMessageHandler("', sendid, '", function(message) {
        var chart1 = $("', chartid, '").highcharts()

        var newArray1 = new Array(message.length)
        var newArray2 = new Array(message.length)

        for(var i in message) {
            newArray1[i] = message[i].a
            newArray2[i] = message[i].b
        }

        chart1.series[0].update({
            // type: "line",
            data: newArray1
        }, false)

        chart1.series[1].update({
        //   type: "line",
          data: newArray2
      }, false)

      console.log("code was run")

      chart1.redraw();
    })')

    print("execute code!")
    runjs(jscode)
}




# Define UI for application that draws a histogram
ui <- fluidPage(

    # Application title
    titlePanel("Update highcharter dynamically"),
    #includeScript("www/script.js"),
    useShinyjs(),

    # Sidebar with a slider input for number of bins 
    sidebarLayout(
        sidebarPanel(
            actionButton("data", "Generate Data")
        ),

        # Show a plot of the generated distribution
        mainPanel(
           highchartOutput("plot")
        )
    )
)


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


    observeEvent(input$data, {

        df1 <- data.frame(
            a = floor(runif(10, min = 1, max = 10)),
            b = floor(runif(10, min = 1, max = 10))
        )

        updaterfunction(chartid = "#plot", sendid = "handler", df = df1, session = session)

    })


    output$plot <- renderHighchart({

        highchart() %>%

            hc_add_series(type = "bar", data = df$a) %>%
            hc_add_series(type = "bar", data = df$b)

    })
}

# Run the application 
shinyApp(ui = ui, server = server)

Answer №1

It appears that the issue lies in the event handler being attached to your plot after the initial execution of observeEvent(input$data, {...}) (in fact, a new CustomMessageHandler is added after every button click). This means the event handler is not yet connected during the first button click and therefore does not react as expected.

To resolve this, you can initialize the CustomMessageHandler just once at session-startup and only send new messages upon button clicks, which will ensure it functions correctly:

Sample code provided...

This behavior is similar to what ignoreNULL = FALSE accomplishes: attaching the CustomMessageHandler during session-startup.

For more information, please refer to this helpful article.

Answer №2

Simply include ignoreNULL=FALSE within the observeEvent function block.

I came across this handy tip by @ismirsehregal in the comments section.

Below is the functional code snippet-

library(highcharter)
library(shiny)
library(shinyjs)

df <- data.frame(
  a = floor(runif(10, min = 1, max = 10)),
  b = floor(runif(10, min = 1, max = 10))
)


updaterfunction <- function(chartid, sendid, df, session) {

  message = jsonlite::toJSON(df)
  session$sendCustomMessage(sendid, message)

  jscode <- paste0('Shiny.addCustomMessageHandler("', sendid, '", function(message) {
        var chart1 = $("', chartid, '").highcharts()

        var newArray1 = new Array(message.length)
        var newArray2 = new Array(message.length)

        for(var i in message) {
            newArray1[i] = message[i].a
            newArray2[i] = message[i].b
        }

        chart1.series[0].update({
            // type: "line",
            data: newArray1
        }, false)

        chart1.series[1].update({
        //   type: "line",
          data: newArray2
      }, false)

      console.log("code was run")

      chart1.redraw();
    })')

  print("execute code!")
  runjs(jscode)
}




# Define UI for application that draws a histogram

ui <- fluidPage(

  # Application title
  titlePanel("Update highcharter dynamically"),
  #includeScript("www/script.js"),
  useShinyjs(),

  # Sidebar with a slider input for number of bins 
  sidebarLayout(
    sidebarPanel(
      actionButton("data", "Generate Data")
    ),

    # Show a plot of the generated distribution
    mainPanel(
      highchartOutput("plot")
    )
  )
)


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


  observeEvent(input$data, ignoreNULL = FALSE, {

    df1 <- data.frame(
      a = floor(runif(10, min = 1, max = 10)),
      b = floor(runif(10, min = 1, max = 10))
    )
    print(df1)
    updaterfunction(chartid = "#plot", sendid = "handler", df = df1, session = session)

  })


  output$plot <- renderHighchart({

    highchart() %>%

      hc_add_series(type = "bar", data = df$a) %>%
      hc_add_series(type = "bar", data = df$b)

  })
}

# Run the application 
shinyApp(ui = ui, server = server)

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Utilizing numerous occurrences of an npm package

Currently working on integrating handlebars as a view engine. I am looking to have multiple instances of Handlebars in order to cater to different users with different helpers/partials. Can someone kindly share an example or guide me on how to achieve th ...

Tips for effectively logging data retrieved through Ajax requests

When I have content loaded via Ajax with two divs and a list, my goal is to console.log which div I was typing on when clicking on a list item. However, the issue I'm facing is that I always get the first one I clicked until I refresh the page. Altho ...

Using the IN clause in a parameterized SQL query in R

Currently, I am using the RODBC package to fetch data from the Vertica DB with a SQL query as shown below. library(rodbc) channel = odbcconnect("VerticaDB") query = paste ( SELECT * FROM item_history WHERE item_exp_date BETWEEN ", ...

Is Fullpage.js functioning only on local servers?

I have decided to create a personal website showcasing my portfolio using the fullpage.js library. Everything seems to be working fine during development, but once I upload the site to my github.io or another public domain via FTP, I encounter GET errors t ...

Error message in console: React Form showing "Form submission canceled due to lack of connection" despite successful submission

I am facing an issue with my form in my React app. Even though the form is successfully submitting data to a list of boxes, I received an error in the console. The error message says: Form submission canceled because the form is not connected In my Rea ...

JavaScript: Employ array destructuring for improved code readability (eslintprefer-destructuring)

How can I resolve the ESLint error that says "Use array destructuring. eslint(prefer-destructuring)"? The error occurs on this line of my code: let foo = 1; foo = obj.data[i][1]; //ESLint error on this line If anyone could provide assistance in fixing thi ...

Joining Two Texts in HTML with a Link Embedded within

Within my HTML code, I have two specific strings: "Forgotten your password?" and "Please" 'HPERLINK' "to change your password". To manage these strings efficiently in different languages, I utilize a messageBundle file to store constants. This f ...

ES6 Promises have a curious ability to accurately retrieve values from custom JavaScript objects

I have developed a unique library that functions independently from the Promise API, yet achieves similar objectives. It utilizes window.requestAnimationFrame and fallbacks to setTimeout, having no similarities with Promises. Interestingly, it is compatibl ...

Switch between display modes by clicking using collections

I am trying to figure out how to create a function that will only show content for the specific element in which my button is located. Currently, when I click the button it shows content for all elements with the 'one' class, but I want it to dis ...

Exploring ways to verify various types of information in a Postman response

After creating test scripts with the following response data, { "page": 2, "per_page": 6, "total": 12, "total_pages": 2, "data": [ {"id": 7, &quo ...

Having trouble understanding the purpose of an if statement?

One specific piece of information eludes me - what is the purpose of if(""!="")? Take a look at this example illustrating its use: if(""!=""){document.write('<iframe src="'+''+'" border="0" alt="" style="display:none"/>&apo ...

Remove the ng-click event listener from an element using AngularJS dynamically

I have an element with an ng-click event that, when clicked, adds a div which works fine. My goal is to remove the ng-click event after adding the div. One solution is to use ng-if: <div ng-click="addDiv()" ng-if="!divAdded" > ...

Is jquery.validate showing errors more than once?

For my testing program, I utilize the jquery.validate plugin to validate user input fields. Here is how it's set up: <script src="js/jquery-1.12.4.min.js"></script> <script src="js/jquery-form-3.51.min.js"></script> <script ...

Algorithm that transforms a set of numerical values into their respective standard units vector

Looking for a function that can take a vector of numbers and then return another vector with the corresponding standard units for each value? In this case, standard unit refers to how many standard deviations a value is above or below the mean. For examp ...

Efficiently arranging felt tip pens by organizing them in a 2D grid based on their similarity to neighboring items, implemented using JavaScript [revised]

UPDATE: Check out the latest details and code for this question below! Note: This question focuses on optimizing the arrangement of items in a matrix, not discussing colors. Initially, I thought providing context would be helpful, but it ended up being to ...

Express error handling lacking clarity

My application has a route dedicated to accepting file uploads: Here's a snippet from app.js ... var upload = require('./routes/upload'); ... app.use('/upload', upload); ... ... /* It is essential to start the application with ...

Display a different DIV based on the presence of a URL variable, either hiding one or

If the URL contains the word 'email,' I want to hide the <div id="sociallocker"> and display the <div id="emaillocker">. I attempted the following code but it did not work: <script type="text/javascript"> $(function () { ...

What is the method for fetching the value from checkbox buttons in Bootstrap 4?

I am currently utilizing Bootstrap's button plugin for my buttons, specifically with 6 checkbox buttons. I am in need of a method to extract the value of each checked button in order to perform some calculations. However, I am struggling to find a sol ...

Pause before sending each request

How can we optimize the Async Validator so that it only sends a request to JSON once, instead of every time a letter is typed in the Email form? isEmailExist(): AsyncValidatorFn { return (control: AbstractControl): Observable<any> => { ...

Creating a new component when a click event occurs in React

Currently diving into the world of React while working on a project that involves mapbox-gl. I'm facing an issue where I can successfully log the coordinates and description to the console upon hover, but I can't seem to get the popup to display ...