2

I have input generated, _1,_2, etc...but I want to know which event is fired.

But both don't work, .clientdata_output_aFired_hidden and d are fired. In my real app other events hidden or linked with reactive values fire also.

And without input$changed it works , but without knowing which is fired.

ui <- fluidPage(
  tags$head(
    tags$script(
      "$(document).on('shiny:inputchanged', function(event) {
          if (event.name != 'changed') {
            Shiny.setInputValue('changed', event.name);
          }
        });"
    )
  ),
  numericInput("a_1", "a_1", 0),
  textInput("a_2", "a_2"),
  textInput("c", "c"),
  textInput("d", "d"),



 p("changedInputs"), textOutput("changedInputs"),br(),
 p("aFired"),textOutput("aFired")
)

server <- function(input, output, session) {
  output$changedInputs <- renderText({
    paste("Outside observer: Latest input fired:", paste(input$changed, collapse = ", "))
  })

  observeEvent({
    lapply(
      (grep(pattern = "a_+[[:digit:]]|c"
      , x = names((input)), value = TRUE)),
      function(x) (input)[[x]]
    )
  }, {
    req(input$changed)
    if (input$changed == "a_1") {
      output$aFired <- renderText("Inside observer: input$a_1 was fired")
    } else  if (input$changed == "a_2") {
      output$aFired <- renderText("Inside observer: input$a_2 was fired")
    } else {
      output$aFired <- renderText((input$changed))
    }
  }, ignoreInit = TRUE)
}

shinyApp(ui, server)
phili_b
  • 885
  • 9
  • 27
  • 1
    I believe one way is to store the most-recent values of those inputs, and inside the `observeEvent` compare the current values with the preserved values; after you do the comparison, you'll need to update the stored values with the current values so that it will work again next time. (This may be the only way.) You can store them either in a regular `list` (in the `server` closure, be sure to use `<<-` when updating values) or a `reactiveValues`, though if the latter make sure you do not introduce more reactivity/dependency. – r2evans Jun 26 '19 at 16:11
  • @r2evans. I upvoted your comment by error on my smartphone : I disagree in fact :p. Thanks anyway for your participation. :) – phili_b Jun 26 '19 at 20:09
  • No worries, @phili_b, you can de-select up-votes on the smartphone just as easily (on mine, at least). I'm curious to see if this works and perhaps work the JS into my habits as well, as I know my comment-recommendation is hack-y. – r2evans Jun 26 '19 at 20:33
  • Btw. [here](https://stackoverflow.com/questions/52930397/get-only-inputids-that-have-changed/52932230#52932230) is a related answer I gave (shiny only) which is quite similar to the scenario described by @r2evans - also a reasonable approach in my eyes. – ismirsehregal Jun 26 '19 at 21:16
  • I understand very well his solution...and your solution in the link :) But it's simpler and cleaner if we can to not reinvent the wheel like your current solution seems ok and don't use comparison. – phili_b Jun 26 '19 at 21:52
  • 1
    While I agree that simpler would be better (and I'm intrigued to try this JS-trick), is there a performance or logical problem with comparing current values? Just curious, I'm happy to extricate myself from this discussion and just `observe`. – r2evans Jun 26 '19 at 22:13
  • 1
    phili_b, I never wrote you wouldn't understand ;-).. I'm pretty sure you do understand a lot.. otherwise you wouldn't be asking these nice questions.. just wanted to add another example. @r2evans I started using the JS-solution because for me it makes sense to use the resources directly provided with shiny (less code to maintain for myself), haven't done any performance tests though. – ismirsehregal Jun 27 '19 at 05:00
  • ismirsehregal, r2evans. In theory comparison could be used, despite more code that I am not fan if there is no real added value except the big one to not use js, but my question was in fact to use [reactive generated checkbox in datatable](https://stackoverflow.com/a/50178470/10489562) from @Shrek Tan. And in this case comparison would be more complicated than getting which checkbox is fired by its event name. But thanks both for this other solution. :) – phili_b Jun 27 '19 at 06:30
  • And behind reactive checkboxes I've added SQL update in SQL Server from the row checked :) – phili_b Jun 27 '19 at 06:34
  • These functionalities (getting fired checkbox and update database) was added, in @Shrek Tan solution, above the `replaceData()` in his `Observe()` replaced by `ObserveEvent()` by grep. :) – phili_b Jun 27 '19 at 06:47
  • I use a small comparison anyway in my case :) – phili_b Jun 27 '19 at 06:54
  • Sure thing - reasonable according the described background - cheers. – ismirsehregal Jun 27 '19 at 07:24

1 Answers1

1

There were two problems that needed to be addressed:

  1. your regular expression |c caught input$changed
  2. you need to use isolate(names(input)) inside the event expression otherwise the observer will fire for every change of names(input)
  3. Edit: use isolate({input$changed}) - see comments (not needed when using reactiveVal())

ui <- fluidPage(
  tags$head(
    tags$script(
      "$(document).on('shiny:inputchanged', function(event) {
          if (event.name != 'changed') {
            Shiny.setInputValue('changed', event.name);
          }
        });"
    )
  ),
  numericInput("a_1", "a_1", 0),
  textInput("a_2", "a_2"),
  textInput("c", "c"),
  textInput("d", "d"),

  p("changedInputs:"), textOutput("changedInputs"), br(),
  p("aFired:"), textOutput("aFired")
)

server <- function(input, output, session) {
  output$changedInputs <- renderText({
    paste("Outside observer: Latest input fired:", paste(input$changed, collapse = ", "))
  })

  observeEvent(eventExpr = {
    lapply(grep(pattern = "^a_+[[:digit:]]$|^c$", x = isolate({names(input)}), value = TRUE), function(x){input[[x]]})
  }, handlerExpr = {
    req(input$changed)
    if (input$changed == "a_1") {
      output$aFired <- renderText("Inside observer: input$a_1 was fired")
    } else  if (input$changed == "a_2") {
      output$aFired <- renderText("Inside observer: input$a_2 was fired")
    } else {
      output$aFired <- renderText({paste("Inside observer:", isolate({input$changed}), "was fired")})
    }
  }, ignoreInit = TRUE)
}

shinyApp(ui, server)

Another Edit: Now I remember where the isolate({input$changed}) got lost (I was sure it worked during my tests...) Initially I suspected the renderText() nested inside the observer might cause the problems, accordingly I implemented a reactiveVal() to print the output. This solution works without isolate({input$changed}):

ui <- fluidPage(
  tags$head(
    tags$script(
      "$(document).on('shiny:inputchanged', function(event) {
          if (event.name != 'changed') {
            Shiny.setInputValue('changed', event.name);
          }
        });"
    )
  ),
  numericInput("a_1", "a_1", 0),
  textInput("a_2", "a_2"),
  textInput("c", "c"),
  textInput("d", "d"),

  p("changedInputs:"), textOutput("changedInputs"), br(),
  p("aFired:"), textOutput("aFired")
)

server <- function(input, output, session) {
  output$changedInputs <- renderText({
    paste("Outside observer: Latest input fired:", paste(input$changed, collapse = ", "))
  })

  myText <- reactiveVal()

  observeEvent(eventExpr = {
    lapply(grep(pattern = "^a_+[[:digit:]]$|^c$", x = isolate({names(input)}), value = TRUE), function(x){input[[x]]})
  }, handlerExpr = {
    req(input$changed)
    if (input$changed == "a_1") {
      myText("Inside observer: input$a_1 was fired")
    } else  if (input$changed == "a_2") {
      myText("Inside observer: input$a_2 was fired")
    } else {
      myText(paste("Inside observer:", input$changed, "was fired"))
    }
  }, ignoreInit = TRUE)

  output$aFired <- renderText({myText()})

}

shinyApp(ui, server)

After finding out about the actual problems and before posting my answer here I reverted back to the version without reactiveVal() (since it is closer to your question) and forgot the isolate. Accordingly you received a mixture of both versions in the first place.

ismirsehregal
  • 30,045
  • 5
  • 31
  • 78
  • Thanks. I will look at your answer at work tomorrow :) – phili_b Jun 26 '19 at 18:28
  • 1
    At home.... It doesn't work. But with `isolate(input$changed)` in `observeEvent()` it does. I will look at in my real shiny app. – phili_b Jun 26 '19 at 19:00
  • 1
    Oh you are right, in some constallations the current answer works in others not (try changing `d` first an than the other inputs and vice versa). During my (hasty) tests I also isolated `input$changed` but thought it wasn't necessary and dropped it again. With `input$changed` isolated it's indeed working stable - I will update the answer accordingly. – ismirsehregal Jun 26 '19 at 20:38
  • 1
    Thank you very much :). It works in my real shiny app (like [I explained in the comment above](https://stackoverflow.com/questions/56776207/get-the-event-which-is-fired-in-shiny-and-with-grep-generated-input?noredirect=1#comment100124338_56776207)) :) ! – phili_b Jun 27 '19 at 07:57
  • I did read your "Another Edit" without `isolate()` but how I have many reactive datas and events, I'm not sure I could to not use it. – phili_b Jun 27 '19 at 08:04
  • 1
    You could use `reactiveValues()` instead of `reactiveVal()` to store multiple reactives. – ismirsehregal Jun 27 '19 at 08:18
  • 1
    In the end, it doesn't matter, both version work - If you have the otherone already implemented with your actual code I'd stick with it (I just wanted to point out why my initial answer here wasn't working - to much playing around on the code..), – ismirsehregal Jun 27 '19 at 08:19
  • I think the origin of the problem was where to put the `isolate()` in the `lapply(grep(` and you find it, and the origin of the solution was your use of JS `Shiny.setInputValue(` because of the apparent absence of getting fired input in `observeEvent()`. – phili_b Jun 27 '19 at 08:36
  • 1
    [github.com/rstudio/shiny #2514: Feature requested: Get the event which is fired in ObserveEvent](https://github.com/rstudio/shiny/issues/2514) – phili_b Jun 27 '19 at 08:43
  • I've made a little edition at the end of your message about grep on non yet existing inputs. (pending peer review). – phili_b Jun 28 '19 at 06:40
  • 1
    Your edit was rejected (not by me). You were specifically asking for a `grep` based solution and this is the answer to it. If you want to warn future readers to use this approach, you should append it to the question. (E.g. as a comment) – ismirsehregal Jun 28 '19 at 11:16
  • __warning addendum 1__ : _TDLR_ : When the inputs are generated they doesn't exist yet in `ObserveEvent()` therefore we have to replace `lapply(grep()` by a lapply list of generated objects from the reactive datasource of the datatable. _In my real Shiny app_: It was for the adaptation of [reactive generated checkbox in datatable](https://stackoverflow.com/questions/48854547/r-shiny-how-to-make-datatable-react-to-checkboxes-in-datatable/50178470#50178470) from @Shrek Tan where his dataframe is replaced in my case by a SQL Query, so the datatable dataframe is empty at the beginning. – phili_b Jun 28 '19 at 13:08
  • __warning addendum 2__ In my real complexe case, `observeEvent()` seems to catch also hidden events and datatable events from JS `Shiny.setInputValue()` not in the list, therefore I've added also a comparison between `input$changed` and the authorized content list of the eventExpr `observeEvent()`. – phili_b Jun 28 '19 at 13:31