【问题标题】:Is it possible to refer to namespace from another module?是否可以从另一个模块引用命名空间?
【发布时间】:2021-03-24 15:42:04
【问题描述】:

我想从第二个服务器模块mod_btn_server2 引用命名空间ns("map")。该模块嵌套在第一个服务器模块mod_btn_server1 中。当我单击“按钮 2”时,点应该显示在地图上,但它们没有。是否可以从嵌套模块中引用"map"

以下是工作示例:

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)

【问题讨论】:

  • 您的设置/您想要达到什么目的?在您的第二个模块moduleServer2 中,您在服务器中定义了一个地图,但不提供任何 UI,因此未显示。如果您想更改模块test-btn 中定义的地图上的某些内容,规范的方法是捕获按钮按下作为模块moduleServer2 的输出,并将其用作您执行操作的test-btn 的输入。但是,您的按钮已经在 test-btn 中定义,所以我不确定您到底想要实现什么。

标签: r shiny leaflet


【解决方案1】:

正如我在上面的评论中提到的,规范的方法是将按钮推送捕获为模块 moduleServer2 的输出,并将其用作执行操作的 test-btn 的输入。

但是,如果您想自己弄乱命名空间(不推荐),您可以使用以下解决方案。我不得不适配leafletProxy函数,因为正常实现会自动添加调用模块的命名空间。这是你不想要的,因为你想使用不同模块的命名空间。

现在使用适合编辑的代码:

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)

这是一个更规范的形式,它适用于模块的正确输入/输出,并且不会与命名空间混淆:

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)

【讨论】:

  • 我更新了我的主代码,现在看起来更好了。基本上,当我单击module2 中的“按钮 2”时,我想在地图上显示点。地图位于module1。我想以某种方式从module2 内渲染主地图上的点。
  • 检查我的编辑;我以前的版本应该已经可以了;现在我也适应了颜色
  • 但是请注意这个方案是违反模块设计原则的!我添加了一个更规范的解决方案
  • 我的意思是你可以并且也许对于你的用例来说它是有意义的(那么最好彻底记录它),但是模块的想法是所有与模块中创建的元素相关的通信都应该通过这个模块的参数来完成
  • 你是对的,这就是为什么我回到我的单模块解决方案,无论如何感谢你对我的问题的有趣方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多