玩!Framework 2.0 - 在 Scala 模板中循环遍历地图?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9946552/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 04:02:53  来源:igfitidea点击:

Play! Framework 2.0 - Looping through a map in a scala template?

templatesscalaplayframeworkplayframework-2.0

提问by wfbarksdale

I have a map representing a table of contents, it contains Chapterkeys and List[Section]values. Right now I am trying to loop through this in my template like this:

我有一个表示目录的地图,它包含Chapter键和List[Section]值。现在我正试图在我的模板中循环这个:

<dl>
@table_of_contents.foreach((e) => {
    <dt>
        @e._1.title
    </dt>
        for(section <- e._2){
        <dd>
            @section.title
        </dd>
        }
})
</dl>

I currently get no output in the <dl>however.

<dl>然而,我目前没有得到任何输出。

I added a println(table_of_contents)statement to the top of the template to ensure that the map did in fact have data and it printed:

println(table_of_contents)在模板顶部添加了一条语句,以确保地图确实有数据并打印出来:

{models.Chapter@1=BeanList size[4] hasMoreRows[false] list[models.Section@1, models.Section@2, models.Section@3, models.Section@4], models.Chapter@2=BeanList size[0] hasMoreRows[false] list[]}

{models.Chapter@1=BeanList size[4] hasMoreRows[false] list[models.Section@1, models.Section@2, models.Section@3, models.Section@4], models.Chapter@2=BeanList size[0] hasMoreRows[false] list[]}

perhaps I need to use an imperative style?

也许我需要使用命令式风格?

UPDATE:

更新:

Still working on this... got this variation to compile but no output.

仍在研究这个……编译这个变体但没有输出。

<dl>
@table_of_contents.foreach{case(a, b) => {
    <dt>
        @a.title
    </dt>
        @displaySections(b)
}}
</dl>

...

@displaySections(sections: List[Section]) = {
  @for(a_section <- sections) {
        <dd>@a_section.title</li>
  }
}

回答by andy petrella

tl;dr

tl;博士

The answers given so far (by @wbarksdale, @PlexQand @Daniel C. Sobralin a comment) are good enough to target the problem described here.

到目前为止给出的答案(@wbarksdale@PlexQ@Daniel C. Sobral在评论中)足以解决这里描述的问题。

But they're missing a real explanation about why the initial code, using foreach, doesn't work.

但是他们没有真正解释为什么使用 的初始代码foreach不起作用。

It cannot work because foreachreturns Unit.

它无法工作,因为foreach返回Unit

Play concepts

玩法概念

Let me give a quick note/recall about how templates work.

让我快速说明/回忆一下模板的工作原理。

The Scala templating system provided by default in Play Framework 2 is indeed built upon FP concepts and thus it uses a lot of immutable structures and so forth.

Play Framework 2 中默认提供的 Scala 模板系统确实建立在 FP 概念之上,因此它使用了许多不可变结构等等。

Moreover, such Scala template (let's say myTemplate.scala.html) will be compiled into a regular Scala objectwhich has an applymethod called. This latter function enables us to callthe object as a function with some parameters (those declared in the first line of the template).

此外,这样的 Scala 模板(比方说myTemplate.scala.html)将被编译成一个普通的 Scala object,它有一个apply被调用的方法。后一个函数使我们能够将对象作为带有一些参数(在模板的第一行中声明的参数)的函数来调用

This objectis also relying on a construction like BaseScalaTemplatewhich is built with an output formatter (Html). This formatter will be able to take stuff (like a String, Unit, Seq[Int], Map[A,B], ...) and render it into HTML code.

object也依赖于BaseScalaTemplate使用输出格式化程序 (Html)构建的结构。此格式化程序将能够获取内容(如 a StringUnitSeq[Int]Map[A,B]、 ...)并将其呈现为 HTML 代码。

Formatting will take place while using the _display_method of BaseScalaTemplate, which returns an instance of the formatted output. This display method will be called in the compiledcode of the .scala.htmlfile in the object's applymethod's body.

格式化将在使用 的_display_方法时进行BaseScalaTemplate,该方法返回格式化输出的一个实例。此显示方法将在对象方法主体中文件的编译代码中调用。.scala.htmlapply

So the body could end like that:

所以身体可以这样结束:

def apply/*1.2*/(testMap:scala.collection.immutable.Map[String, Int]):play.api.templates.Html = 
  _display_ {
    Seq[Any](
      _display_(
        Seq[Any](
          /*3.2*/testMap/*3.9*/.map/*3.13*/ { e =>
            _display_(Seq[Any](_display_(Seq[Any](/*5.3*/e))))
          }
        )
      )
    )
  }

See? The _display_calls aren't mutating anything but they are composed in such a way that apply itself will return an instance of the formatted code (Html)!

看?这些_display_调用不会改变任何东西,但它们的组合方式是 apply 本身将返回格式化代码 ( Html) 的一个实例!

That gives us the clue...

这给了我们线索......

yeah blah blah... now why?

是啊等等……现在为什么?

After those lightnings given about the Play internals, we can now tackle the real question: why the hell is the ideomatic Scala code provided in the question post isn't working... read, doesn't output anything at all.

在关于 Play 内部的那些闪电之后,我们现在可以解决真正的问题:为什么问题帖子中提供的 Scala 代码不起作用......阅读,根本不输出任何内容。

It's pretty simple, when using foreachon a Map, you're indeed loopingover the items and adaptingthem to Html. But these computation won't be usable by the templating system because they are enclosed in the foreach's loop. That is foreachhas to be used when side-effects are required for each item in the sequence... And return Unitwhen it's done.

这很简单,当foreach在 a 上使用时Map,您确实在循环项目并将它们调整为 Html。但是这些计算不会被模板系统使用,因为它们被包含在foreach's 循环中。这是foreach有当需要对序列中的每个项目的副作用要使用......并返回Unit时,它的完成。

Since, the templating system will try to _display_the result of foreachon the given Mapit will simply render/format Unitand thus an empty String!

因为,模板系统将尝试在给定_display_的结果foreach上,Map它只会渲染/格式化Unit,因此是一个空的String!

To conclude, just use mapwhich will return a new sequence holding the adapteditems, the Htmlinstance.

总而言之,只需使用mapwhich 将返回一个包含适应项目的新序列,即Html实例。

Hmmm and what about the for?

嗯,那for呢?

Yeah, you're right... Based on what has been said, why are the answers that proposed to used a forloop working, since without yielding a value, a foris equivalent to foreach!? (Introducing yieldwill end in a map-like behavior)

是的,你是对的......根据已经说过的,为什么建议使用for循环的答案有效,因为没有产生值, afor等价于foreach!? (引入yield将以类似map行为结束)

The answer is in the code... The Template compiler will prepend the yieldkeyword to the for's body -- check this out here. :-D

答案就在代码中...模板编译器会yieldfor's body'前面加上关键字- 请在此处查看。:-D

Et voilà, it works too, since the generated stuffs in the for's body will be attached to a returned sequence after it has finished.

等等,它也可以工作,因为在for主体中生成的东西将在完成后附加到返回的序列。

回答by wfbarksdale

The solution I came up with looked like this. Basically it just avoids using functional programming which I am ok with for the time being, but I would still really like to see a working solution using scala functional style.

我想出的解决方案看起来像这样。基本上它只是避免使用我暂时可以接受的函数式编程,但我仍然非常希望看到使用 scala 函数式风格的可行解决方案。

<dl>
@for((key, value) <- table_of_contents) {
    <dt>
        @key.getTitle
    </dt>
        @displaySections(value)
}
</dl>

@displaySections(sections: List[Section]) = {
  @for(a_section <- sections) {
        <dd>@a_section.getTitle</li>
  }
}

回答by PlexQ

Play in Scala uses the functional nature of Scala very well. Change this to a map that returns the elements, and it should work.

Play in Scala 很好地利用了 Scala 的功能特性。将其更改为返回元素的地图,它应该可以工作。

<dl>
@table_of_contents.map( case(k,v) => {
    <dt>
        @k.title
    </dt>
    @v.map { section =>
        <dd>
            @section.title
        </dd>
    }
})
</dl>

As per suggestion above, with the case, it turns it into a partial function that does what we want rather nicely!

根据上面的建议,在这种情况下,它将它变成了一个可以很好地完成我们想要的部分功能!