Home > Enterprise >  How to update input in shiny modlue from another shiny module?
How to update input in shiny modlue from another shiny module?

Time:07-22

I've got two shiny modules, with updateTextInput() in the first one. I want to update textInput() in the second module, when button from the first is clicked. I know it's because those modules are in different namespaces but I can't figure out how to communicate modules.

Reprex below :)

library(shiny)

firstUI <- function(id) {
  ns <- NS(id)
  tagList(
    actionButton(ns("update"), "Update 1st and 2nd module"),
    textInput(ns("first"), "Update me pls1", value = "Clear me!")
    
  )
}
firstServer <- function(id) {
  moduleServer(id, function(input, output, session) {
    observeEvent(input$update, {
      updateTextInput(session = session, "first", value = "")
      updateTextInput(session = session,"second", value = "")
    })
})
}
secondUI <- function(id) {
  ns <- NS(id)
  tagList(
    textInput(ns("second"), "Update me pls", value = "Clear me!")
  )
}
secondServer <- function(id) {
  moduleServer(id, function(input, output, session) {
    observeEvent(input$update, {
      updateTextInput(session = session, "first", value = "")
      updateTextInput(session = session,"second", value = "")
    })
})
}

ui <- fluidPage(
  firstUI("module_one"),
  secondUI("module_two")
)

server <- function(input, output, session) {
  firstServer("module_one")
  secondServer("module_two")
}

shinyApp(ui, server)

CodePudding user response:

You can do it by making the first input$update reactive, then returning that value and making it reactive to the second server module. This way the second server module is "listening" to the change in the first one.

library(shiny)

firstUI <- function(id) {
  ns <- NS(id)
  tagList(
    actionButton(ns("update"), "Update 1st and 2nd module"),
    textInput(ns("first"), "Update me pls1", value = "Clear me!")
    
  )
}
firstServer <- function(id) {
  moduleServer(id, function(input, output, session) {
    observeEvent(input$update, {
      updateTextInput(session = session, "first", value = "")
      updateTextInput(session = session,"second", value = "")
    })
    reactive(input$update)
  })
}
secondUI <- function(id) {
  ns <- NS(id)
  tagList(
    textInput(ns("second"), "Update me pls", value = "Clear me!")
  )
}
secondServer <- function(id, clear) {
  moduleServer(id, function(input, output, session) {
    observeEvent(clear(), {
      updateTextInput(session = session, "first", value = "")
      updateTextInput(session = session,"second", value = "")
    })
  })
}

ui <- fluidPage(
  firstUI("module_one"),
  secondUI("module_two")
)

server <- function(input, output, session) {
  clear <- reactive(firstServer("module_one"))
  secondServer("module_two", clear())
}

shinyApp(ui, server)

CodePudding user response:

A roundabout way would be to use shinyjs to trigger the updating manually.

library(shiny)
library(shinyjs)

firstUI <- function(id) {
  ns <- NS(id)
  tagList(
    actionButton(ns("update"), "Update 1st and 2nd module"),
    textInput(ns("first"), "Update me pls1", value = "Clear me!")
    
  )
}
firstServer <- function(id) {
  moduleServer(id, function(input, output, session) {
    observeEvent(input$update, {
      updateTextInput(session = session, "first", value = "")
      runjs('document.getElementById("module_two-second").value = ""')
    })
  })
}

secondUI <- function(id) {
  ns <- NS(id)
  tagList(
    textInput(ns("second"), "Update me pls", value = "Clear me!")
  )
}

secondServer <- function(id) {
  moduleServer(id, function(input, output, session) {
    # Code not needed in here for now
  })
}

ui <- fluidPage(
  useShinyjs(),
  firstUI("module_one"),
  secondUI("module_two")
)

server <- function(input, output, session) {
  firstServer("module_one")
  secondServer("module_two")
}

shinyApp(ui, server)

Shiny modules work by giving each element a unique id by pasting [module_name]-[element_id] together in the html frontend, so each module server can correctly identify which it should be talking to. The first server can find and talk to module_two-second when passed that id directly. Ideally there might be a way of doing this within the Shiny code itself though.

Edit: fix within Shiny by passing parent_session (without shinyjs)

The updateTextInput call can indeed find module_two-second itself if it can look outside of its own session environment. To achieve this, you can pass the parent_session as the argument to updateTextInput (defined in firstServer function definition and passed as parent_session = session in the server body):

library(shiny)

firstUI <- function(id) {
  ns <- NS(id)
  tagList(
    actionButton(ns("update"), "Update 1st and 2nd module"),
    textInput(ns("first"), "Update me pls1", value = "Clear me!")
    
  )
}
firstServer <- function(id, parent_session) {
  moduleServer(id, function(input, output, session) {
    observeEvent(input$update, {
      updateTextInput(session = session, "first", value = "")
      updateTextInput(session = parent_session, "module_two-second", value = "")
    })
  })
}

secondUI <- function(id) {
  ns <- NS(id)
  tagList(
    textInput(ns("second"), "Update me pls", value = "Clear me!")
  )
}

secondServer <- function(id) {
  moduleServer(id, function(input, output, session) {
    # Code not needed in here for now
  })
}

ui <- fluidPage(
  firstUI("module_one"),
  secondUI("module_two")
)

server <- function(input, output, session) {
  firstServer("module_one", parent_session = session)
  secondServer("module_two")
}

shinyApp(ui, server)
  • Related