I write an app which I try to modularize. In general, I added leaflet map to my app's body (in main module) and what I want to do is to write some other modules which refer to my main map (showing/hiding points on a map and other spatial operations). I try to refer to this map (being in main module) from other modules. In example below I passed map as reactive expression from main module but when I press button showing points on the map then the error shows up:
Error in if: missing value where TRUE/FALSE needed
Is it possible to pass map to another module at all? And use leafletProxy there?
Here is reproducible example:
library(shiny)
library(dplyr)
library(sf)
library(leaflet)
moduleServer <- function(id, module) {
callModule(module, id)
}
# Main module - UI 1 #
mod_btn_UI1 <- function(id) {
ns <- NS(id)
tagList(
leafletOutput(ns("map")),
mod_btn_UI2(ns("other"))
)
}
# Main module - Server 1 #
mod_btn_server1 <- function(id){
moduleServer(id, function(input, output, session) {
ns <- NS(id)
# here I pass map as reactive
passMap = reactive({input$map})
coords <- quakes %>%
sf::st_as_sf(coords = c("long","lat"), crs = 4326)
output$map <- leaflet::renderLeaflet({
leaflet::leaflet() %>%
leaflet::addTiles() %>%
leaflet::setView(172.972965,-35.377261, zoom = 4) %>%
leaflet::addCircleMarkers(
data = coords,
stroke = FALSE,
radius = 6)
})
mod_btn_server2("other", passMap)
})
}
# Other module - UI 2 #
mod_btn_UI2 <- function(id) {
ns <- NS(id)
tagList(
actionButton(inputId = ns("btn"), label = "show points")
)
}
# Other module - Server 2 #
mod_btn_server2 <- function(id, passMap){
moduleServer(id, function(input, output, session) {
ns <- NS(id)
coords <- quakes %>%
sf::st_as_sf(coords = c("long","lat"), crs = 4326)
observeEvent(input$btn, {
leaflet::leafletProxy(passMap()) %>%
leaflet::addCircleMarkers(
data = coords,
stroke = TRUE,
color = "red",
radius = 6)
})
})
}
# Final app #
ui <- fluidPage(
tagList(
mod_btn_UI1("test-btn"))
)
server <- function(input, output, session) {
mod_btn_server1("test-btn")
}
shinyApp(ui = ui, server = server)
It seems that you need to pass down the leaflet proxy as a reactive to the module for it to work.
Added a leaflet proxy instead of input name for the variable passMap in module mod_btn_server1:
proxymap <- reactive(leafletProxy('map'))
mod_btn_server2("other", proxymap)
In mod_btn_server2 you have then to change Passmap to be the leafletproxy itself removing the call to leafletproxy:
observeEvent(input$btn, {
passMap() %>%
leaflet::addCircleMarkers(
data = coords,
stroke = TRUE,
color = "red",
radius = 6)
})
The full code here:
library(shiny)
library(dplyr)
library(sf)
library(leaflet)
moduleServer <- function(id, module) {
callModule(module, id)
}
# Main module - UI 1 #
mod_btn_UI1 <- function(id) {
ns <- NS(id)
tagList(
leafletOutput(ns("map")),
mod_btn_UI2(ns("other"))
)
}
# Main module - Server 1 #
mod_btn_server1 <- function(id){
moduleServer(id, function(input, output, session) {
ns <- NS(id)
# here I pass map as reactive
passMap = reactive({input$map})
coords <- quakes %>%
sf::st_as_sf(coords = c("long","lat"), crs = 4326)
output$map <- leaflet::renderLeaflet({
leaflet::leaflet() %>%
leaflet::addTiles() %>%
leaflet::setView(172.972965,-35.377261, zoom = 4) %>%
leaflet::addCircleMarkers(
data = coords,
stroke = FALSE,
radius = 6)
})
proxymap <- reactive(leafletProxy('map'))
mod_btn_server2("other", proxymap)
})
}
# Other module - UI 2 #
mod_btn_UI2 <- function(id) {
ns <- NS(id)
tagList(
actionButton(inputId = ns("btn"), label = "show points")
)
}
# Other module - Server 2 #
mod_btn_server2 <- function(id, passMap){
moduleServer(id, function(input, output, session) {
ns <- NS(id)
coords <- quakes %>%
sf::st_as_sf(coords = c("long","lat"), crs = 4326)
observeEvent(input$btn, {
passMap() %>%
leaflet::addCircleMarkers(
data = coords,
stroke = TRUE,
color = "red",
radius = 6)
})
})
}
# Final app #
ui <- fluidPage(
tagList(
mod_btn_UI1("test-btn"))
)
server <- function(input, output, session) {
mod_btn_server1("test-btn")
}
shinyApp(ui = ui, server = server)
Related
i am using R6 to pass variables across and along the main server and server modules, following Jiwan Heo's approach as described in his article.
Using a simplified example (without graph to minimise code length) of the mentioned article, this works:
library(shiny)
library(dplyr)
library(ggplot2)
library(shinydashboard)
IrisR6 <- R6::R6Class(
"IrisR6",
public = list(
n_rows = NULL,
multiplier = NULL,
orig_data = iris,
res_data = NULL,
manip_data = function(dat) {
dat %>%
head(self$n_rows) %>%
mutate(Sepal.Length = Sepal.Length * self$multiplier)
}
)
)
mod_manip_ui <- function(id) {
ns <- NS(id)
tagList(
numericInput(ns("n_rows"),
"Number of rows to display",
value = 10,
min = 1,
max = 150),
numericInput(ns("multiplier"),
"A random calculation",
value = 1,
min = 1,
max = 10),
actionButton(ns("go"), "Go!")
)
}
mod_manip_server <- function(id, r6) {
moduleServer(id, function(input, output, session) {
observeEvent(input$go, {
r6$n_rows <- input$n_rows
r6$multiplier <- input$multiplier
new_data <- r6$manip_data(dat = r6$orig_data)
r6$res_data <- new_data
gargoyle::trigger("update_iris")
})
})
}
mod_table_ui <- function(id, r6) {
ns <- NS(id)
tagList(
textOutput(ns("text")),
###### This doesn't work ######
# shinydashboard::valueBox(
# value = r6$n_rows,
# subtitle = "nr of rows"
# ),
###############################
###### This works: ######
shinydashboard::valueBox(
value = rnorm(1),
subtitle = "random nr"
),
#########################
tableOutput(ns("table"))
)
}
mod_table_server <- function(id, r6) {
moduleServer(id, function(input, output, session) {
observeEvent(gargoyle::watch("update_iris"), {
output$text <- renderText(paste("Multiplier:", r6$multiplier))
output$table <- renderTable({
req(!is.null(r6$res_data))
r6$res_data
})
})
})
}
ui <- fluidPage(
column(12, mod_manip_ui("mod_manip_1")),
column(6, mod_table_ui("mod_table_1"))
)
server <- function(session, input, output) {
r6 <- IrisR6$new()
gargoyle::init("update_iris")
mod_manip_server("mod_manip_1", r6 = r6)
mod_table_server("mod_table_1", r6 = r6)
mod_graph_server("mod_graph_1", r6 = r6)
}
shinyApp(ui, server)
Now if you look to mod_table_ui() and use r6$n_rows as an argument of valueBox() the code doesn't work (see the commented part inside the mod_table_ui() of the code above).
What is the best approach to pass external variables defined in the main server of server modules, as arguments of functions used inside the main or modular ui?
As suggested by Stéphane's comment above, using renderUI() allows to pass variables available in the server to arguments of functions used normally at the UI. So replacing mod_table_ui() and mod_table_server() from the code above with the functions below will do the job:
mod_table_ui <- function(id) {
ns <- NS(id)
tagList(
textOutput(ns("text")),
uiOutput(ns('renderUIway')),
tableOutput(ns("table"))
)
}
mod_table_server <- function(id, r6) {
moduleServer(id, function(input, output, session) {
observeEvent(gargoyle::watch("update_iris"), {
output$text <- renderText(paste("Multiplier:", r6$multiplier))
output$table <- renderTable({
req(!is.null(r6$res_data))
r6$res_data
})
output$renderUIway <- renderUI({
tagList(
###### This now also works! ######
shinydashboard::valueBox(
value = r6$n_rows,
subtitle = "nr of rows"
)
###############################
###### This works: ######
# shinydashboard::valueBox(
# value = rnorm(1),
# subtitle = "random nr"
# )
#########################
)
})
})
})
}
I am trying to create a editable shiny DT table module. It works well when I pass in iris data.
However, when I tried to pass a reactive value, the module does not work. Does anyone had similar experience before ? Could you share your thought?
library(shiny)
library(DT)
editableUI<-function(id){
ns <- NS(id)
DT::dataTableOutput(ns("mod_table"))
}
editableUIServer<-function(id,data,disable_col,change){
moduleServer(id,
function(input,output,session){
print('hi')
v<-reactiveValues(data = data)
print('here we got v')
print(v$data)
output$mod_table <- renderDT(v$data,
editable = list(target = 'cell',
disable = list(columns=c(disable_col))))
proxy = dataTableProxy('mod_table')
observeEvent(input$mod_table_cell_edit, {
info = input$mod_table_cell_edit
v$data <<- editData(v$data, info)
replaceData(proxy, v$data, resetPaging = FALSE)
})
observeEvent(change(),{
v$data<<-data
})
return(v)
}
)}
# Define UI for application that draws a histogram
ui <- fluidPage(
fluidPage(
fluidRow(pickerInput('spec',label = 'Species',choices = unique(as.character(iris$Species)),selected = "versicolor")
),
fluidRow(editableUI("test")))
)
# Define server logic required to draw a histogram
server <- function(input, output) {
data<-reactive({
iris %>% filter(Species %in% c(input$spec))
})
observe({print(data())})
v<-reactive({editableUIServer("test",data(),c(1), change_ppg = reactive(input$spec))})
}
# Run the application
shinyApp(ui = ui, server = server)
I changed a couple of lines and now it works. Not sure why it was wrong in the first place, though
library(shiny)
library(DT)
editableUI<-function(id){
ns <- NS(id)
DT::dataTableOutput(ns("mod_table"))
}
editableUIServer<-function(id,data,disable_col){
moduleServer(id,
function(input,output,session){
print('hi')
v<-reactiveValues(data = data)
print('here we got v')
output$mod_table <- renderDT(v$data,
editable = list(target = 'cell',
disable = list(columns=c(disable_col))))
proxy = dataTableProxy('mod_table')
observeEvent(input$mod_table_cell_edit, {
info = input$mod_table_cell_edit
v$data <<- editData(v$data, info)
replaceData(proxy, v$data, resetPaging = FALSE)
})
return(v)
}
)}
# Define UI for application that draws a histogram
ui <- fluidPage(
fluidPage(
fluidRow(pickerInput('spec',label = 'Species',choices = unique(as.character(iris$Species)),selected = "versicolor")
),
fluidRow(editableUI("test")))
)
# Define server logic required to draw a histogram
server <- function(input, output) {
data<-reactive({
iris %>% filter(Species %in% c(input$spec))
})
# v<-reactive({editableUIServer("test",data =iris,c(1), change_ppg = reactive(input$spec))})
v<- reactive(editableUIServer("test",data(),c(1)))
observe(print(v()$data))
}
# Run the application
shinyApp(ui = ui, server = server)
Below is a Shiny app in which a Highcharter map is displayed.
When a user clicks a country, the name of the country is displayed below the map.
The app below works when it does not use modules. When implemented using a module, the country selected does not display anymore.
library(shiny)
library(highcharter)
library(dplyr)
# MODULE UI
module_ui <- function(id){
ns <- NS(id)
div(
highchartOutput(ns("hcmap")),
verbatimTextOutput(ns("country"))
)
}
# SERVER UI
module_server <- function(id){
ns <- NS(id)
moduleServer(id, function(input, output, session){
# Data
data_4_map <- download_map_data("custom/world-robinson-highres") %>%
get_data_from_map() %>%
select(`hc-key`) %>%
mutate(value = round(100 * runif(nrow(.)), 2))
# Map
click_js <- JS("function(event) {Shiny.onInputChange('hcmapclick',event.point.name);}")
output$hcmap <- renderHighchart({
hcmap(map = "custom/world-robinson-highres",
data = data_4_map,
value = "value",
joinBy = "hc-key",
name = "Pop",
download_map_data = F) %>%
hc_colorAxis(stops = color_stops()) %>%
hc_plotOptions(series = list(events = list(click = click_js)))
})
# Clicked country
output$country <- renderPrint({
print(input$hcmapclick)
})
})
}
# APP UI
ui <- fluidPage(
tags$script(src = "https://code.highcharts.com/mapdata/custom/world-robinson-highres.js"),
fluidRow(
module_ui(id = "moduleID")
)
)
# APP SERVER
server <- function(input, output, session) {
module_server(id = "moduleID")
}
shinyApp(ui, server)
EDIT
Adding the module ID to the Shiny.onInputChange function as follows, does not solve the problem.
click_js <- JS("function(event) {console.log(event.point.name); Shiny.onInputChange('moduleID-hcmapclick', event.point.name);}")
You have to add the module ID to your call back function. We can do this programmatically by using the module id in paste0 inside the JS() call:
library(shiny)
library(highcharter)
library(dplyr)
# MODULE UI
module_ui <- function(id){
div(
highchartOutput(ns("hcmap")),
verbatimTextOutput(ns("country"))
)
}
# SERVER UI
module_server <- function(id){
moduleServer(id, function(input, output, session){
# Data
data_4_map <- download_map_data("custom/world-robinson-highres") %>%
get_data_from_map() %>%
select(`hc-key`) %>%
mutate(value = round(100 * runif(nrow(.)), 2))
# Map
click_js <- JS(paste0("function(event) {Shiny.onInputChange('",id,"-hcmapclick',event.point.name);}"))
output$hcmap <- renderHighchart({
hcmap(map = "custom/world-robinson-highres",
data = data_4_map,
value = "value",
joinBy = "hc-key",
name = "Pop",
download_map_data = F) %>%
hc_colorAxis(stops = color_stops()) %>%
hc_plotOptions(series = list(events = list(click = click_js)))
})
# Clicked country
output$country <- renderPrint({
print(input$hcmapclick)
})
})
}
# APP UI
ui <- fluidPage(
tags$script(src = "https://code.highcharts.com/mapdata/custom/world-robinson-highres.js"),
fluidRow(
module_ui(id = "moduleID")
)
)
# APP SERVER
server <- function(input, output, session) {
module_server(id = "moduleID")
}
shinyApp(ui, server)
The goal of this module is create a reactive barplot that changes based on the output of a data selector module. Unfortunately the barplot does not update. It's stuck at the first variable that's selected.
I've tried creating observer functions to update the barplot, to no avail. I've also tried nesting the selector server module within the barplot module, but I get the error: Warning: Error in UseMethod: no applicable method for 'mutate' applied to an object of class "c('reactiveExpr', 'reactive', 'function')"
I just need some way to tell the barplot module to update whenever the data it's fed changes.
Barplot Module:
#UI
barplotUI <- function(id) {
tagList(plotlyOutput(NS(id, "barplot"), height = "300px"))
}
#Server
#' #param data Reactive element from another module: reactive(dplyr::filter(austin_map, var == input$var))
barplotServer <- function(id, data) {
moduleServer(id, function(input, output, session) {
#Data Manipulation
bardata <- reactive({
bar <-
data |>
mutate(
`> 50% People of Color` = if_else(`% people of color` >= 0.5, 1, 0),
`> 50% Low Income` = if_else(`% low-income` >= 0.5, 1, 0)
)
total_av <- mean(bar$value)
poc <- bar |> filter(`> 50% People of Color` == 1)
poc_av <- mean(poc$value)
lowincome <- bar |> filter(`> 50% Low Income` == 1)
lowincome_av <- mean(lowincome$value)
bar_to_plotly <-
data.frame(
y = c(total_av, poc_av, lowincome_av),
x = c("Austin Average",
"> 50% People of Color",
"> 50% Low Income")
)
return(bar_to_plotly)
})
#Plotly Barplot
output$barplot <- renderPlotly({
plot_ly(
x = bardata()$x,
y = bardata()$y,
color = I("#00a65a"),
type = 'bar'
) |>
config(displayModeBar = FALSE)
})
})
}
EDIT :
Data Selector Module
dataInput <- function(id) {
tagList(
pickerInput(
NS(id, "var"),
label = NULL,
width = '100%',
inline = FALSE,
options = list(`actions-box` = TRUE,
size = 10),
choices =list(
"O3",
"Ozone - CAPCOG",
"Percentile for Ozone level in air",
"PM2.5",
"PM2.5 - CAPCOG",
"Percentile for PM2.5 level in air")
)
)
}
dataServer <- function(id) {
moduleServer(id, function(input, output, session) {
austin_map <- readRDS("./data/austin_composite.rds")
austin_map <- as.data.frame(austin_map)
austin_map$value <- as.numeric(austin_map$value)
list(
var = reactive(input$var),
df = reactive(austin_map |> dplyr::filter(var == input$var))
)
})
}
Simplified App
library(shiny)
library(tidyverse)
library(plotly)
source("barplot.r")
source("datamod.r")
ui = fluidPage(
fluidRow(
dataInput("data"),
barplotUI("barplot")
)
)
server <- function(input, output, session) {
data <- dataServer("data")
variable <- data$df
barplotServer("barplot", data = variable())
}
shinyApp(ui, server)
As I wrote in my comment, passing a reactive dataset as an argument to a module server is no different to passing an argument of any other type.
Here's a MWE that illustrates the concept, passing either mtcars or a data frame of random values between a selection module and a display module.
The critical point is that the selection module returns the reactive [data], not the reactive's value [data()] to the main server function and, in turn, the reactive, not the reactive's value is passed as a parameter to the plot module.
library(shiny)
library(ggplot2)
# Select module
selectUI <- function(id) {
ns <- NS(id)
selectInput(ns("select"), "Select a dataset", c("mtcars", "random"))
}
selectServer <- function(id) {
moduleServer(
id,
function(input, output, session) {
data <- reactive({
if (input$select == "mtcars") {
mtcars
} else {
tibble(x=runif(10), y=rnorm(10), z=rbinom(n=10, size=20, prob=0.3))
}
})
return(data)
}
)
}
# Barplot module
barplotUI <- function(id) {
ns <- NS(id)
tagList(
selectInput(ns("variable"), "Select variable:", choices=c()),
plotOutput(ns("plot"))
)
}
barplotServer <- function(id, plotData) {
moduleServer(
id,
function(input, output, session) {
ns <- NS(id)
observeEvent(plotData(), {
updateSelectInput(
session,
"variable",
choices=names(plotData()),
selected=names(plotData()[1])
)
})
output$plot <- renderPlot({
# There's an irritating transient error as the dataset
# changes, but handling it would
# detract from the purpose of this answer
plotData() %>%
ggplot() + geom_bar(aes_string(x=input$variable))
})
}
)
}
# Main UI
ui <- fluidPage(
selectUI("select"),
barplotUI("plot")
)
# Main server
server <- function(input, output, session) {
selectedData <- selectServer("select")
barplotServer <- barplotServer("plot", plotData=selectedData)
}
# Run the application
shinyApp(ui = ui, server = server)
I want to refer to the namespace ns("map") from second server module mod_btn_server2. This module is nested in first server module mod_btn_server1. When I click 'Button 2' points should show up on a map but they didn't. Is it possible at all to refer to "map" from nested module?
Here's working example:
library(shiny)
library(mapboxer)
library(dplyr)
library(sf)
library(leaflet)
moduleServer <- function(id, module) {
callModule(module, id)
}
# UI 1 #
mod_btn_UI1 <- function(id) {
ns <- NS(id)
tagList(
actionButton(ns("btn1"), "Button 1"),
mod_btn_UI2(ns("moduleServer2")),
leafletOutput(ns("map"))
)
}
# Server 1 #
mod_btn_server1 <- function(id){
moduleServer(id, function(input, output, session) {
ns <- NS(id)
coords <- quakes %>%
sf::st_as_sf(coords = c("long","lat"), crs = 4326)
mod_btn_server2("moduleServer2", coords) # here is nested module2
output$map <- leaflet::renderLeaflet({
leaflet::leaflet() %>%
leaflet::addTiles() %>%
leaflet::setView(172.972965,-35.377261, zoom = 4) %>%
leaflet::addCircleMarkers(
data = coords,
stroke = FALSE,
radius = 6)
})
observeEvent(input$btn1, {
leaflet::leafletProxy("map", data = coords) %>%
leaflet::addCircles()
})
})
}
# Module 2 - UI #
mod_btn_UI2 <- function(id){
ns <- NS(id)
actionButton(ns("btn2"), "Button 2"),
}
# Module 2 - server #
mod_btn_server2 <- function(id, dataMod){
moduleServer(id, function(input, output, session) {
ns <- NS(id)
output$map <- leaflet::renderLeaflet({
leaflet::leaflet() %>%
leaflet::addTiles() %>%
leaflet::setView(172.972965,-35.377261, zoom = 4) %>%
leaflet::addCircleMarkers(
data = dataMod,
stroke = TRUE,
color = "red",
radius = 6)
})
# and here I refer to 'map' located in the first module
observeEvent(input$btn2, {
leaflet::leafletProxy("map", data = dataMod) %>%
leaflet::addCircles()
})
})
}
# App #
ui <- fluidPage(
tagList(
mod_btn_UI1("test-btn"))
)
server <- function(input, output, session) {
mod_btn_server1("test-btn")
}
shinyApp(ui = ui, server = server)
As I mentioned in my comment above, the canonical way would be to capture the button push as an output of module moduleServer2 and use this as an input for test-btn where you perform the action.
However, if you want to mess around with the namespaces by yourself (not recommended), you can use the following solution. I had to adapt the leafletProxy function, because the normal implementation automatically adds the namespace of the calling module. This is what you don't want, because you want to use the namespace of a different module.
Now with code adapted to the edit:
library(shiny)
library(mapboxer)
library(dplyr)
library(sf)
library(leaflet)
leafletProxy2 <- function (mapId, session = shiny::getDefaultReactiveDomain(),
data = NULL, deferUntilFlush = TRUE)
{
if (is.null(session)) {
stop("leafletProxy must be called from the server function of a Shiny app")
}
structure(list(session = session, id = mapId, x = structure(list(),
leafletData = data), deferUntilFlush = deferUntilFlush,
dependencies = NULL), class = "leaflet_proxy")
}
# UI 1 #
mod_btn_UI1 <- function(id) {
ns <- NS(id)
tagList(
actionButton(ns("btn1"), "Button 1"),
mod_btn_UI2(ns("moduleServer2")),
leafletOutput(ns("map"))
)
}
# Server 1 #
mod_btn_server1 <- function(id){
moduleServer(id, function(input, output, session) {
coords <- quakes %>%
sf::st_as_sf(coords = c("long","lat"), crs = 4326)
mod_btn_server2("moduleServer2", coords) # here is nested module2
output$map <- leaflet::renderLeaflet({
leaflet::leaflet() %>%
leaflet::addTiles() %>%
leaflet::setView(172.972965,-35.377261, zoom = 4) %>%
leaflet::addCircleMarkers(
data = coords,
stroke = FALSE,
radius = 6)
})
observeEvent(input$btn1, {
leaflet::leafletProxy("map", data = coords) %>%
leaflet::addCircles()
})
})
}
# Module 2 - UI #
mod_btn_UI2 <- function(id){
ns <- NS(id)
actionButton(ns("btn2"), "Button 2")
}
# Module 2 - server #
mod_btn_server2 <- function(id, dataMod, btn){
moduleServer(id, function(input, output, session) {
# and here I refer to 'map' located in the first module
observeEvent(input$btn2, {
leafletProxy2("test-btn-map", data = dataMod) %>%
leaflet::addCircles(stroke = TRUE,
color = "red")
})
})
}
# App #
ui <- fluidPage(
tagList(
mod_btn_UI1("test-btn"))
)
server <- function(input, output, session) {
mod_btn_server1("test-btn")
}
shinyApp(ui = ui, server = server)
Here is a more canonical form that works with the correct input/output of modules and doesn't mess with namespaces:
library(shiny)
library(mapboxer)
library(dplyr)
library(sf)
library(leaflet)
# UI 1 #
mod_btn_UI1 <- function(id) {
ns <- NS(id)
tagList(
actionButton(ns("btn1"), "Button 1"),
mod_btn_UI2(ns("moduleServer2")),
leafletOutput(ns("map"))
)
}
# Server 1 #
mod_btn_server1 <- function(id){
moduleServer(id, function(input, output, session) {
ns <- NS(id)
coords <- quakes %>%
sf::st_as_sf(coords = c("long","lat"), crs = 4326)
external_btn <- mod_btn_server2("moduleServer2", coords) # here is nested module2
output$map <- leaflet::renderLeaflet({
leaflet::leaflet() %>%
leaflet::addTiles() %>%
leaflet::setView(172.972965,-35.377261, zoom = 4) %>%
leaflet::addCircleMarkers(
data = coords,
stroke = FALSE,
radius = 6)
})
observeEvent(input$btn1, {
leaflet::leafletProxy("map", data = coords) %>%
leaflet::addCircles()
})
observeEvent(external_btn(), {
leaflet::leafletProxy("map", data = coords) %>%
leaflet::addCircles(stroke = TRUE,
color = "red")
})
})
}
# Module 2 - UI #
mod_btn_UI2 <- function(id){
ns <- NS(id)
actionButton(ns("btn2"), "Button 2")
}
# Module 2 - server #
mod_btn_server2 <- function(id, dataMod){
moduleServer(id, function(input, output, session) {
return(reactive(input$btn2))
})
}
# App #
ui <- fluidPage(
tagList(
mod_btn_UI1("test-btn"))
)
server <- function(input, output, session) {
mod_btn_server1("test-btn")
}
shinyApp(ui = ui, server = server)