zip style @repeat over nested form

2019-03-31 04:20发布

问题:

@repeat is enormously useful; however, I am hitting a road block with nested forms.

I need to produce a form for a schedule of games, which has 2 attributes, schedule data (game date, time, location, opponent) and submitting team notes (e.g. "due to a winter storm the game on January 7th has been moved to January 9th in...Hawaii ;-)")

Form mapping is based on:

case class Schedule(
  composite: Seq[Composite], 
  note: Seq[ScheduleNote]
)

and then to display the form in a template I have:

@repeat(_form("composite"), min=numGames) { f=>
  @inputDate(f("gameDate"), 'id-> "gameDate", '_label-> "Game Date")
  ....
}
@repeat(_form("note"), min=numGames) { f=>
  @inputDate(f("gameNote"), '_label-> "Game Notes")
  ....
}

of course game notes need to be paired with game data, which will not happen in the above since it looks like I need to @repeat composite game data and notes separately.

It would be really, really nice to: @repeat(_form("composite").zip(_form("note")), min=numGames) { case(fc,fn)=>

over the nested form elements.

Is there anyway I can pull this off? Looking at the source it appears not, but perhaps with a pimp my library it's possible (or, since I'm building against 2.1, hack something in place until the framework supports what seems to be a limitation)

回答1:

EDIT
Actually, my original attempt doubled the number of produced fields; this one generates the correct number of fields:

object repeat2 {
  import play.api.data.Field, play.api.templates.Html
  def apply(field: (Field,Field), min: Int = 1)(f: (Field,Field) => Html) = {
    field match{ case(a,b)=>
      (0 until math.max(
        if (a.indexes.isEmpty) 0 else a.indexes.max + 1, min)
      ).map(i => f.apply(a("["+i+"]"), b("["+i+"]")) )
    }
  }
}

Still TBD if edit form maps form data values properly....

ORIGINAL
Experimenting, this compiles:

// in a form utility object
object repeat2 {
  import play.api.data.Field, play.api.templates.Html
  def apply(field: (Field,Field), min: Int = 1)(f: Field => Html) = {
    field match{ case(a,b)=>
      (0 until math.max(
        if (a.indexes.isEmpty) 0 else a.indexes.max + 1, min)
      ).map(i => f(a("["+i+"]")) + f(b("["+i+"]")) )
    }
  }
}

// then, importing above in a template
@repeat2( (_form("composite"), _form("note")), min=5) { f=>
  @inputDate(f("gameDate"), 'id-> "gameDate", '_label-> "Game Date")
  ...
  @inputDate(f("gameNote"), '_label-> "Game Notes")
}

and does generate game data and notes together as desired.

As to whether or not it works on form edit, TBD ;-)