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

如何基于嵌套列表在Shiny中递归创建嵌套div元素

在Shiny中基于嵌套列表递归生成div元素

问题描述

我需要基于一个嵌套列表的text字段,在Shiny里递归创建嵌套的div元素。我的嵌套列表示例如下:

nodes <- list(
      list(
        text = "RootA",
        state = list(loaded = TRUE, opened = TRUE, selected = FALSE, disabled = FALSE),
        children = list(
          list(
            text = "ChildA1",
            state = list(loaded = TRUE, opened = TRUE, selected = FALSE, disabled = FALSE)
          ),
          list(
            text = "ChildA2",
            state = list(loaded = TRUE, opened = TRUE, selected = FALSE, disabled = FALSE)
          )
        )
      ),
      list(
        text = "RootB",
        state = list(loaded = TRUE, opened = TRUE, selected = FALSE, disabled = FALSE),
        children = list(
          list(
            text = "ChildB1",
            state = list(loaded = TRUE, opened = TRUE, selected = FALSE, disabled = FALSE),
            children = list(
              list(
                text = "ChildB1a",
                state = list(loaded = TRUE, opened = TRUE, selected = FALSE, disabled = FALSE)
              ),
              list(
                text = "ChildB1b",
                state = list(loaded = TRUE, opened = TRUE, selected = FALSE, disabled = FALSE)
              )
            )
          ),
          list(
            text = "ChildB2",
            state = list(loaded = TRUE, opened = TRUE, selected = FALSE, disabled = FALSE)
          )
        )
      )
    )

我试过用purrr::map处理,但它只能生成顶层的div元素,代码如下:

purrr::map(nodes, function(x){
  if("text" %in% names(x)){
    div(x$text)
  }
})

我的预期输出是生成嵌套的div结构(style参数仅作示例):

library(shiny)

divs <- list(
  div("RootA",
      div("ChildA1"),
      div("ChildA2"),
      style = "background-color: aqua;"
  ),
  div("RootB",
      div("ChildB1",
          div("ChildB1a"),
          div("ChildB1b")),
      div("ChildB2"),
      style = "background-color: lightgreen;"
    )
)
   
ui <- fluidPage(
  divs
)

server <- function(input, output, session) {
  
}

shinyApp(ui, server)

解决方案

要实现递归生成嵌套div,你需要编写一个递归函数,遍历每个节点的children字段,将子节点生成的div作为当前div的子元素传入。结合purrr::map可以轻松实现这一逻辑:

递归函数实现

library(shiny)
library(purrr)

# 递归生成嵌套div的函数
create_nested_div <- function(node) {
  # 提取当前节点的文本内容
  text_content <- node$text
  
  # 处理子节点:若存在children字段,递归生成子div列表
  child_divs <- if ("children" %in% names(node)) {
    map(node$children, create_nested_div)
  } else {
    list()
  }
  
  # 创建当前div,嵌入文本和子div,可自定义样式
  div(
    text_content,
    !!!child_divs,  # 使用!!!展开子div列表作为参数
    style = "padding-left: 15px; border-left: 1px solid #ccc; margin: 5px 0;"  # 层级区分样式,可按需修改
  )
}

完整Shiny示例

将递归函数应用到nodes列表,生成完整的UI结构:

library(shiny)
library(purrr)

nodes <- list(
      list(
        text = "RootA",
        state = list(loaded = TRUE, opened = TRUE, selected = FALSE, disabled = FALSE),
        children = list(
          list(
            text = "ChildA1",
            state = list(loaded = TRUE, opened = TRUE, selected = FALSE, disabled = FALSE)
          ),
          list(
            text = "ChildA2",
            state = list(loaded = TRUE, opened = TRUE, selected = FALSE, disabled = FALSE)
          )
        )
      ),
      list(
        text = "RootB",
        state = list(loaded = TRUE, opened = TRUE, selected = FALSE, disabled = FALSE),
        children = list(
          list(
            text = "ChildB1",
            state = list(loaded = TRUE, opened = TRUE, selected = FALSE, disabled = FALSE),
            children = list(
              list(
                text = "ChildB1a",
                state = list(loaded = TRUE, opened = TRUE, selected = FALSE, disabled = FALSE)
              ),
              list(
                text = "ChildB1b",
                state = list(loaded = TRUE, opened = TRUE, selected = FALSE, disabled = FALSE)
              )
            )
          ),
          list(
            text = "ChildB2",
            state = list(loaded = TRUE, opened = TRUE, selected = FALSE, disabled = FALSE)
          )
        )
      )
    )

create_nested_div <- function(node) {
  text_content <- node$text
  child_divs <- if ("children" %in% names(node)) {
    map(node$children, create_nested_div)
  } else {
    list()
  }
  div(
    text_content,
    !!!child_divs,
    style = "padding-left: 15px; border-left: 1px solid #ccc; margin: 5px 0;"
  )
}

# 生成所有顶层div
divs <- map(nodes, create_nested_div)

ui <- fluidPage(
  h3("递归生成的嵌套div结构"),
  div(divs, style = "padding: 10px;")
)

server <- function(input, output, session) {}

shinyApp(ui, server)

关键说明

  • !!!child_divs:利用rlang的非标准求值语法,将子div列表展开为div()函数的多个参数,实现嵌套结构。
  • 递归逻辑:函数会自动检查当前节点是否包含children字段,若存在则递归调用自身处理每个子节点,生成的子div会嵌入当前div中。
  • 样式自定义:示例中的style仅用于区分层级,你可以根据需求修改背景色、缩进、边框等样式。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 18:20:00