在Play 2.5中使用Scalaform映射select输入-使用DTO对象

在Play 2.5中使用Scalaform映射select输入-使用DTO对象,scala,playframework,Scala,Playframework,我在Play2.5中有以下控制器,取自github repo raul782/play commerce class CategoryController(implicit inj: Injector) extends Controller { private val categoryService = inject[CategoryService] def viewAllCategories = Action { implicit request => val all

我在Play2.5中有以下控制器,取自github repo raul782/play commerce

class CategoryController(implicit inj: Injector) extends Controller {

  private val categoryService = inject[CategoryService]

  def viewAllCategories = Action { implicit request =>
    val allCategories = categoryService.findAll.map(Category.toDTO(_))
    Ok(views.html.product.categoryList(allCategories))
  }

  def listCategories = Action { implicit request =>
    val allCategories = categoryService.findAll.map(Category.toDTO(_))
    Ok(RequestUtil.toJsonString(allCategories)).as(JSON)
  }

  def viewCategoryForm(categoryId: Long) = Action { implicit request =>
    Ok(views.html.product.category(views.forms.product.categoryForm,      categoryService.findAll))
  }

  def addCategoryForm = Action { implicit request =>
    Ok(views.html.product.category(views.forms.product.categoryForm,   categoryService.findAll))
  }

  def addCategory = Action { implicit request =>
    import views.forms.product.categoryForm
    categoryForm.bindFromRequest.fold(
      formWithErrors => {
        Ok(views.html.product.category(formWithErrors, categoryService.findAll))
    },
      categoryDTO => {
        play.Logger.debug(request.body.asFormUrlEncoded.toString)
        play.Logger.debug(categoryDTO.toString)
        val category = Category.fromDTO(categoryDTO)
        categoryService.save(category)

        Redirect(routes.CategoryController.viewAllCategories())
      }
    )
  }
}
我的category.html.scala设置为:

@(categoryForm: Form[dtos.product.CategoryDTO], categoryDTOs:     Seq[models.product.Category])(implicit request:     play.api.mvc.Request[play.api.mvc.AnyContent])

@import models.product.Category
@import helper._
@import views.html.forms.inputHidden
@import views.forms.FormHelpers.bootstrapVerticalFormFieldConstructor

@title = @{categoryForm(Category.Id).value.
                    map(_ => Messages("forms.categories.update")).
                    getOrElse(Messages("forms.categories.add"))}

@action = @{categoryForm(Category.Id).value.
                    map(_ =>     controllers.product.routes.CategoryController.updateCategory()).
                        getOrElse(controllers.product.routes.CategoryController.addCategory())}

@categoryOptions = @{categoryDTOs.map(category => category.id.toString -> category.name)}

@main(title = title) {
<div class="col-sm-2">
    <h2>@title</h2>
</div>
<div class="col-sm-7">
    @form(action = action, args = 'class -> "well") {
        @inputHidden(categoryForm(Category.Id))
        @inputText(categoryForm(Category.Name), 'class -> "form-control")
        @select(categoryForm(Category.ParentCategory), categoryOptions,      'class -> "form-control", '_default -> "--- select ---")
        <div class="form-group">
            <button type="submit" class="btn btn-primary">               
                @Messages("forms.save")
            </button>
            <a href="@controllers.product.routes.CategoryController.viewAllCategories()" class="btn btn-default">@Messages("forms.cancel")</a>
        </div>
    }
</div>
}
我的分类如下:

package views.forms

import play.api.data._
import play.api.data.Forms._
import dtos.product.CategoryDTO

package object product {
  val categoryForm = Form(
    mapping(
      "id" -> optional(longNumber(strict = true)),
      "name" -> text(minLength = 1, maxLength = 255),
      "parentCategoryId" -> list(optional(longNumber))
    ) (CategoryDTO.apply) (CategoryDTO.unapply)
  )
}
package dtos.product

case class CategoryDTO(id: Option[Long], name: String, parentCategoryId: List[Option[Long]]) {
  var ancestry: Seq[CategoryDTO] = Nil
  var parentCategoryName: Option[String] = None
}
package models.product

import models.BaseEntity
import javax.persistence.Entity
import javax.persistence.Column
import javax.persistence.Access
import javax.persistence.AccessType
import java.util.LinkedHashSet
import javax.persistence.ManyToOne
import javax.persistence.OneToMany
import java.util.{Set => JSet}
import models.IdentifierProperty
import dtos.product.CategoryDTO

@Entity
class Category extends BaseEntity {

  def this(id: Option[Long]) = {
   this()
   this.id = id
  }

  @Column(nullable = false, length = 255)
  var name: String = _

  @Access(AccessType.PROPERTY)
  @ManyToOne(targetEntity = classOf[Category])
  var parentCategory: Option[Category] = None

  @OneToMany(mappedBy = "parentCategory")
  var childCategories: JSet[Category] = new LinkedHashSet

  def ancestors = {
    traverseAncestry(parentCategory)  
  }

  private def traverseAncestry(parentCategory: Option[Category], lst: List[Category] = Nil): Seq[Category] = {
    parentCategory  match {
      case Some(currentParentCategory) => traverseAncestry(currentParentCategory.parentCategory, currentParentCategory :: lst)
      case _ => lst
    }
  }

  protected def getParentCategory() = {
    parentCategory.getOrElse(null)
  }

  protected def setParentCategory(category: Category) {
    parentCategory = Option(category)
  }
}

object Category extends IdentifierProperty {
  val Name = "name"
  val ParentCategory = "parentCategoryId"
  val ChildCategories = "childCategories"

  def toDTO(category: Category, withAncestry: Boolean = false): CategoryDTO =   {
    val dto = CategoryDTO(category.id, category.name, category.parentCategory.map( c => c.id).toList)
    dto.parentCategoryName = category.parentCategory.map(_.name)
    dto.ancestry = if(withAncestry) { 
      category.ancestors.map(Category.toDTO(_)) 
    } else { 
      Nil
    }
    dto
  }

  def fromDTO(dto: CategoryDTO) = {
    val category = new Category
    category.name = dto.name
    category.parentCategory = dto.parentCategoryId match {
      case Nil => None
      case head::_ => head.map(id => new Category(Some(id)))
    }
    category
  }
}
我的分类模型如下:

package views.forms

import play.api.data._
import play.api.data.Forms._
import dtos.product.CategoryDTO

package object product {
  val categoryForm = Form(
    mapping(
      "id" -> optional(longNumber(strict = true)),
      "name" -> text(minLength = 1, maxLength = 255),
      "parentCategoryId" -> list(optional(longNumber))
    ) (CategoryDTO.apply) (CategoryDTO.unapply)
  )
}
package dtos.product

case class CategoryDTO(id: Option[Long], name: String, parentCategoryId: List[Option[Long]]) {
  var ancestry: Seq[CategoryDTO] = Nil
  var parentCategoryName: Option[String] = None
}
package models.product

import models.BaseEntity
import javax.persistence.Entity
import javax.persistence.Column
import javax.persistence.Access
import javax.persistence.AccessType
import java.util.LinkedHashSet
import javax.persistence.ManyToOne
import javax.persistence.OneToMany
import java.util.{Set => JSet}
import models.IdentifierProperty
import dtos.product.CategoryDTO

@Entity
class Category extends BaseEntity {

  def this(id: Option[Long]) = {
   this()
   this.id = id
  }

  @Column(nullable = false, length = 255)
  var name: String = _

  @Access(AccessType.PROPERTY)
  @ManyToOne(targetEntity = classOf[Category])
  var parentCategory: Option[Category] = None

  @OneToMany(mappedBy = "parentCategory")
  var childCategories: JSet[Category] = new LinkedHashSet

  def ancestors = {
    traverseAncestry(parentCategory)  
  }

  private def traverseAncestry(parentCategory: Option[Category], lst: List[Category] = Nil): Seq[Category] = {
    parentCategory  match {
      case Some(currentParentCategory) => traverseAncestry(currentParentCategory.parentCategory, currentParentCategory :: lst)
      case _ => lst
    }
  }

  protected def getParentCategory() = {
    parentCategory.getOrElse(null)
  }

  protected def setParentCategory(category: Category) {
    parentCategory = Option(category)
  }
}

object Category extends IdentifierProperty {
  val Name = "name"
  val ParentCategory = "parentCategoryId"
  val ChildCategories = "childCategories"

  def toDTO(category: Category, withAncestry: Boolean = false): CategoryDTO =   {
    val dto = CategoryDTO(category.id, category.name, category.parentCategory.map( c => c.id).toList)
    dto.parentCategoryName = category.parentCategory.map(_.name)
    dto.ancestry = if(withAncestry) { 
      category.ancestors.map(Category.toDTO(_)) 
    } else { 
      Nil
    }
    dto
  }

  def fromDTO(dto: CategoryDTO) = {
    val category = new Category
    category.name = dto.name
    category.parentCategory = dto.parentCategoryId match {
      case Nil => None
      case head::_ => head.map(id => new Category(Some(id)))
    }
    category
  }
}
当我填写表单并提交请求时,表单将被保存,但仅保存名称,而不保存父类别。 我注意到发送的select输入值是一些(15),其中15是parentCategoryId

最初,parentCategoryId的categoryForm映射只是可选的(longNumber),但我也无法捕获值

我切换到list(可选(longNumber)),因为当您使用select输入时,文档会这么说

但是仍然没有运气,没有任何建议,或者我的实现有什么问题


谢谢

从您的问题和一个类别只有一个父类别名称的事实判断,我假设您只需要父类别的(单个)id?这是相当典型的“外键”情况。
select
元素(默认情况下)仅选择单个元素

  • 在映射中,使用
    可选(longNumber)
    而不是列表
  • 在数据中,使其成为
    选项[Int]
    而不是集合
如果您正在查找父类别列表,那么您当前的代码更接近您需要的代码,但select本身无法做到这一点—您将需要一个multiselect javascript库

还有,你为什么用var而不是val