Вызов функции в качестве параметра при вызове шаблона в Play

В игре/Скале; Я смущен тем, почему это не скомпилируется:

   @(tabNames: Seq[String])

   @isActive(idx: Int) = {
       @if(idx < 1) {
           "active"
       } else {
           ""
       }
   }

   <ul class="nav nav-tabs">
       @for((tab, idx) <- tabNames.zipWithIndex) {
           @views.html.generic.navLi("tab" + idx.toString, tab, isActive(idx))
       }
   </ul>

Ошибка гласит:

найдено: play.twirl.api.HtmlFormat.Appendable [ошибка] (которая расширяется до) play.twirl.api.Html [ошибка] требуется: String [ошибка]
@views.html.generic.navLi("tab" + idx.toString, вкладка, isActive(idx))

Он не распознает вызов isActive в вызове шаблона, и я пробовал несколько вариантов, например. @isActive(idx), isActive(@idx) ${isActive(idx)} (как предложено здесь ) и т. д. Этот шаблон создает панель навигации, передавая имена вкладок и проверяя, должно ли nav li быть active (настроено по имени класса/JS).

Просто кажется, что синтаксис должен быть другим при вызове функции в другом вызове шаблона - я не могу понять это правильно.


person jesus g_force Harris    schedule 23.11.2017    source источник


Ответы (2)


Документация проводит различие между

  • многоразовый блок кода
  • повторно используемый чистый блок кода

Обратите внимание на тонкую разницу в использовании @ между

@isActive(idx: Int) = {
  @if(...

@isActive(idx: Int) = @{
  if(...

Повторно используемый блок чистого кода может иметь произвольный тип возвращаемого значения. В вашем случае, чтобы иметь String в качестве возвращаемого типа, вы можете написать:

   @isActive(idx: Int) = @{
       if(idx < 1) {
           "active"
       } else {
           ""
       }
   }
person Mario Galic    schedule 26.11.2017

документация Play в этой области не очень понятна ; хотя, безусловно, возможно и полезно объявить «функции» в шаблоне twirl, тип возвращаемого значения, по-видимому, заблокирован (фактически) Html, то есть ваш «блок» (как он упоминается в документации) должен отображать HTML.

Самое быстрое решение, предложенное в документации, — перенести логику в класс Scala; для чего-то столь же простого object - это самый простой способ сделать это, то есть:

package views

object ViewHelper {

  def isActive(idx: Int):String = {
    if(idx < 1) {
      "active"
    } else {
      ""
    }
  }
}

и:

<ul class="nav nav-tabs">
   @for((tab, idx) <- tabNames.zipWithIndex) {
       @views.html.generic.navLi("tab" + idx.toString, tab, ViewHelper.isActive(idx))
   }
</ul>

К преимуществам этого подхода относятся тестируемость и возможность повторного использования. Просто будьте осторожны, чтобы не увлечься тем, что может сделать ViewHelper - поиск в базе данных и т. д. здесь плохая идея! :-)

person millhouse    schedule 24.11.2017
comment
Да, документация определенно легкая по этой теме. Вы уверены, что пакет views — лучшее место для размещения этого object? Также я заметил, что вы не импортировали ViewHelper - это правильно? спасибо - person jesus g_force Harris; 25.11.2017
comment
Поместив его в views, мне не нужно его импортировать :-) - person millhouse; 25.11.2017