我正在尝试将书籍对象从视图传递到路径,然后将其发送到控制器中进行计算。我的代码如下:
bookList.scala.html
@(books: java.lang.Iterable[Book])
@main("BookList"){
<div class="row">
@for(book <- books.iterator()){
<div class="col-sm-6 col-md-4">
<div class="thumbnail" style="height: 435px">
...
<p><a href="@routes.Application.buy(book)" class="btn btn-primary" role="button"
style="vertical-align:bottom">Order now!</a>
</div>
</div>
</div>
}
</div>
}
路由
...
GET /order controllers.Application.buy(book: models.Book)
...
然而,它给了我一个错误:No QueryString binder found for type models.Book. Try to implement an implicit QueryStringBindable for this type.
我尝试将路线更改为:
GET /order controllers.Application.buy(book)
它还返回了一个错误:
type mismatch; found : String required: models.Book
答案 0 :(得分:9)
这不是播放路由的工作原理。 Play路由器解析URL或查询字符串中的变量,并通过QueryBindable
类型类将它们转换为本机类型。你应该有更多这样的东西:
<强>路由强>
GET /order/:bookid controllers.Application.buy(bookid: String)
行动应该是:
<强> Application.scala 强>
def buy(bookid: String) = Action { request =>
// Look up book by id here.
Ok(views.html.thanks("you bought a book!"))
}
这样的模板:
<强> bookList.scala.html 强>
@for(book <- books.iterator()) {
...
<a href="@routes.Application.buy(book.id)" class="btn btn-primary"
}
当然,如果你的模型的ID不是String
,你需要修改路线的参数类型
更新 - 使用表格/ POST替代
使用POST方法的表单是更好的解决方案,或者用户每次单击URL时都会购买另一本书,并且ID将被公开。查看forms文档。你的模板是这样的:
@for(book <- books.iterator()) {
...
<form method="post">
<div>@book.name</div>
<input type="hidden" value="@book.id"/><button type="submit">buy</button>
</form>
}
答案 1 :(得分:4)
您不能简单地将对象(bean)作为url查询参数传递。
默认您只能将简单类型定义为参数类型。请仔细阅读play的rounting文档 - &gt; http://www.playframework.com/documentation/2.2.x/ScalaRouting尤其是参数类型
但是play框架有可能“学习”如何将特定的url解释为bean。 你发布了错误的女巫信息。负责此 QueryStringBindable - &gt; http://www.playframework.com/documentation/2.2.1/api/java/play/mvc/QueryStringBindable.html
简而言之,就像在文档中一样,当你定义这样的类时:
class Book implements QueryStringBindable<Book> {
public String title;
public int numpages;
public Option<Pager> bind(String key, Map<String, String[]> data) {
if (data.contains(key + ".title" && data.contains(key + ".numpages") {
try {
title = data.get(key + ".title")[0];
numpages = Integer.parseInt(data.get(key + ".numpages")[0]);
return Some(this);
} catch (NumberFormatException e) {
return None();
}
} else {
return None();
}
}
public String unbind(String key) {
return key + ".title=" + title + "&" + key + ".numpages=" + numpages;
}
public String javascriptUnbind() {
return "function(k,v) {\n" +
" return encodeURIComponent(k+'.title')+'='+v.title+'&'+encodeURIComponent(k+'.numpages')+'='+v.numpages;\n" +
"}";
}
}
然后你可以定义路线,如:
GET /order controllers.Application.buy(p: Book)
然后您可以在浏览器中运行,例如链接:
localhost:9000/?p.title=SomeTitle&p.numpages=235
在购买控制器中,您将获得p参数作为Book类实例。
我没有测试这段代码,这是在java中。但你应该明白这一点。
答案 2 :(得分:0)
您可以定义自定义PathBindable以自动将id转换为book对象,并将其传递给controller中的buy方法。请参阅PathBindable,another example
注意:我会在与Book模型相同的文件中定义PathBindable。
答案 3 :(得分:0)
我曾经使用PathBindable在UUID中解析的简单示例。
我不必解析UUID并确保它们在所有控制器/操作代码中都很好地形成,所以我在路由器级别使用它。我使用UUIDP(P for param)来不污染UUID名称空间。
case class UUIDP(key: String, value: String, uuid: UUID)
object UUIDP {
implicit def pathBinder(implicit intBinder: PathBindable[String]) = new PathBindable[UUIDP] {
val uuidRegex = "([0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12})".r
override def bind(key: String, value: String): Either[String, UUIDP] = {
value match {
// sanity check, prevent errors, is this really a UUID?
case uuidRegex(c) => {
//error check too, binary value can be invalid
try { Right(UUIDP(key,value,UUID.fromString(value))) }
catch {
case ex : IllegalArgumentException => Left("Unparsable UUID")
}
}
case _ => Left("Invalid UUID Format")
}
}
// in case we need to remove this from the request
override def unbind(key: String, user: UUIDP): String = {
intBinder.unbind(key, user.value)
}
}
}
GET /items/:id @com.foo.MyController.getItem(id: UUIDP)
def getItem(sessionId: UUIDP) = Action(parse.text) { request =>
Ok(s"Fetching item [${sessionId.uuid}]")
}