我想为一个节点使用条件showIf产生一组重复的节点,如下所示:
div<id = "parent">
div<id = "child1">Child 1</div>
div<id = "child2">Child 2</div>
div<>Optional text for child 2</div>
</div>
为了产生这个,我可能会使用重复函数,如下所示:
div(id := "parent",
repeat(seqProp)(child =>
div(id := child.get.id),
showIf(child.transform(_.otionalText.nonEmpty))(div(child.optionalText.get))
)
)
但是无论我用什么方式尝试编写此代码,都无法编译以上代码。有人可以推荐我一个好方法吗?
注意。如果我有Seq [Frag],则可以按该序列调用render。但是showIf会生成一个Binding,它似乎隐式转换为Modifier,但没有转换为Frag。
Modifier
(因此可以在进行任何更改时相应地替换它们)。首先,repeat
仅跟踪结构更改,因此您需要组合2个绑定。为避免泄漏,在这种情况下,可以使用repeatWithNested
。
第二,scalatags.generic.LowPriUtil#OptionFrag
允许您渲染Option
节点,因此您无需担心showIf
。
考虑到这一点,假设您具有一些模型类和序列:
case class C(id: String, optionalText: Option[String])
val seqProp = SeqProperty(C("a", Some("")))
您可以写:
div( id := "parent", repeatWithNested(seqProp)((childProp, nested) => div( nested(produce(childProp)(child => Seq( div(id := child.id)(child.id).render, child.optionalText.render, ))) ).render) )
不幸的是,这会产生一个额外的嵌套div
,但是会正确地对结构和值补丁做出反应。您可以在此处查看此代码的运行情况:https://scalafiddle.io/sf/BHG388f/0
[如果您确实想避免这种情况,则必须牺牲其中的一些属性,例如通过在
produce
上使用seqProp
并将parent
创建为其在构建器内部的根节点。
我将详细说明我的情况,以更好地解释上下文。我有以下课程:
case class MenuNode(item: MenuItem, subNodes: Seq[MenuNode])
并且我需要一个递归函数,该函数迭代所有将它们转换为DOM结构的节点。除此之外,我需要处理这些节点的单击事件,并根据它们是否已展开来展开/折叠菜单。此函数将需要访问属性,因为它将修改实际节点。因此,为此,我添加了一个额外的转换,以从我的SeqProperty移到普通属性。所以我想出的解决方案类似于以下递归函数:
def renderItems(property: Property[MenuNode]): Modifier = { val seqProp = property.transformToSeq(_.subNodes, (sns: Seq[MenuNode]) => property.get.copy(subNodes = sns)) produce(seqProp) { nodes => div(nodes.zipWithIndex.flatMap { case (node, i) => val nodeProp = property.transform(_.subNodes(i), (node: MenuNode) => property.get.copy(subNodes = property.get.subNodes.replace(i, node))) Seq( button(cls := "menu-button", onclick := { () => handleClick(nodeProp) }, node.item.label ) ) ++ node.subNodes.nonEmpty.option { div(cls := "sub-menu", renderItems(nodeProp)) } }).render } }
注意。option {...}是我添加的一项增强功能,用于将布尔值转换为Option。