如何基于嵌套列表在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
相关产品推荐
相关产品推荐

