You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Shiny中实现Leaflet套索选择功能遇prependContent错误求助

修正Shiny中Leaflet套索选择联动表格的代码

问题背景

开发含大量点位的Shiny应用,需通过套索选择点位并在表格展示对应数据。参考套索选择功能实现时,出现错误提示:Ignoring prepended content; prependContent can't be used in a Shiny render call。

错误原因

在Shiny的renderLeaflet调用中使用htmlwidgets::prependContent加载外部脚本不符合Shiny的渲染逻辑,需调整脚本加载方式。

修正后的完整代码

library(shiny)
library(leaflet)
library(crosstalk)
library(DT)
library(dplyr)
library(htmltools)
library(summarywidget)

# 准备数据
data_2 <- data.frame(
  ID = c(1:8),
  Name1 = c("A", "A", "A", "C", "B", "B", "A", "B"),
  Name2 = c("a", "b", "b", "a", "b", "a", "b", "c"),
  Value1 = c(12,43,54,34,23,77,44,22),
  Value2 = c(6,5,2,7,5,6,4,3),
  Lat = c(51.1, 51.6, 57.3, 52.4, 56.3, 54.3, 60.4, 49.2),
  Lon = c(5, -3, -2, -1, 4, 3, -5, 0)
) %>%
  mutate(
    lab_DB = case_when(
      Name1 == unique(Name1)[1] ~ "blue",
      Name1 == unique(Name1)[2] ~ "green",
      Name1 == unique(Name1)[3] ~ "red"
    )
  )

# 定义Shiny应用
ui <- fluidPage(
  # 在页面头部加载leaflet-lasso脚本,替代prependContent
  tags$head(
    tags$script(src = "https://unpkg.com/leaflet-lasso@2.2.4/dist/leaflet-lasso.umd.min.js")
  ),
  titlePanel("Leaflet套索选择联动表格"),
  fluidRow(
    column(width = 6, leafletOutput("map")),
    column(width = 6, DTOutput("table"))
  )
)

server <- function(input, output, session) {
  # 创建共享数据对象
  sdf <- SharedData$new(data_2, key = ~ID, group = "SharedDataqwertyui")
  
  # 渲染地图
  output$map <- renderLeaflet({
    leaflet() %>%
      addTiles() %>%
      addMarkers(data = sdf, group = "test", layerId = ~ID) %>%
      htmlwidgets::onRender("
        function(el, x) {
          var sheet = window.document.styleSheets[0];
          sheet.insertRule('.selectedMarker { filter: hue-rotate(135deg); }', sheet.cssRules.length);

          var map = this;
          const lassoControl = L.control.lasso(options={'position':'topleft'}).addTo(map);

          function resetSelectedState() {
                map.eachLayer(layer => {
                    if (layer instanceof L.Marker) {
                        layer.setIcon(new L.Icon.Default());
                    } else if (layer instanceof L.Path) {
                        layer.setStyle({ color: '#3388ff' });
                    }
                });
            }
            function setSelectedLayers(layers) {
                resetSelectedState();
                let ids = [];

                layers.forEach(layer => {
                    if (layer instanceof L.Marker) {
                      layer.setIcon(new L.Icon.Default({ className: 'selected selectedMarker'}));
                    } else if (layer instanceof L.Path) {
                        layer.setStyle({ color: '#ff4620' });
                    }

                    ids.push(layer.options.layerId);
                });
                ct_filter.set(ids);
            }

            var ct_filter = new crosstalk.FilterHandle('SharedDataqwertyui');
            ct_filter.setGroup('SharedDataqwertyui');

            var ct_sel = new crosstalk.SelectionHandle('SharedDataqwertyui');
            ct_sel.setGroup('SharedDataqwertyui');

            map.on('mousedown', () => {
                ct_filter.clear();
                ct_sel.clear();
                resetSelectedState();
            });
            map.on('lasso.finished', event => {
                setSelectedLayers(event.layers);
            });

            lassoControl.setOptions({ intersect: true});

            var clearSel = function(){
                ct_filter.clear();
                ct_sel.clear();
                resetSelectedState();
            }

            document.getElementById('clearbutton').onclick = clearSel;
        }") %>%
      addEasyButton(
        easyButton(
          icon = "fa-ban",
          title = "Clear Selection",
          id = "clearbutton",
          onClick = JS("function(btn, map){ return; }")
        )
      )
  })
  
  # 渲染表格
  output$table <- renderDT({
    datatable(sdf, width = "100%", editable = TRUE, 
              caption = tags$caption("Mean of Value1: ", 
                                    summarywidget(sdf, statistic = 'mean', column = 'Value1')))
  })
}

shinyApp(ui, server)

关键修正点

  • 移除htmlwidgets::prependContent,改用Shiny UI的tags$head加载leaflet-lasso脚本,避免渲染冲突。
  • 将原静态代码重构为标准Shiny应用结构(UI+Server),适配Shiny的运行逻辑。
  • 保留原套索选择、点位高亮、表格联动及清除选择的功能逻辑。

内容的提问来源于stack exchange,提问作者CCurtis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 06:04:59