在上一篇文章中 ,我介绍了在Play 2 Java应用程序中使用JavaScript路由。 这是Scala版本。 为了独立起见,它几乎是前一篇文章的副本。
Play 2中最好的功能之一是框架中可以生成JavaScript路由,以使基于AJAX的客户端代码更易于维护,但似乎并未广泛涉及。 在这里,我将介绍在基于Scala的Play 2应用程序中使用此功能。
总览
在JavaScript中看到发出AJAX请求的调用是很常见的。 通常,使用jQuery之类的库来提供支持。 例如,给定一条路线
GET /foo controllers.Application.getAll()
可以使用简写$ .get方法发出GET请求。
$.get("/foo", function( data ) {
// do something with the response
});
在需要参数的情况下,变体需要更多的工作。
GET /foo controllers.Application.get(personId: Long, taskId: Long)
var personId = $('#person').val();
var taskId = $('#item').val();
$.get("/foo?person=" + personId + '&task=' + taskId,
function( data ) {
// do something with the response
});
所有这些似乎都与Play应用程序的简单风格相去甚远,后者的交互是惯用的且类型安全的。 幸运的是,Play提供了一项称为JavaScript路由的功能。 这使我们可以使用后端生成的JS对象,因此我们可以将上面的代码替换为
var personId = $('#person').val();
var taskId = $('#item').val();
appRoutes.controllers.Application.get(personId, taskId).ajax({
success: function( data ) {
// do something with the response
}
})
这将导致以personId和taskId作为请求参数的GET请求到/ foo。
GET /foo?personId=personId&taskId=taskId
将路由文件更改为使用RESTful风格的URL / foo /:personId /:taskId将导致以下调用,而无需更改JS代码:
GET /foo/personId/taskId
让我们来看看实现这一目标需要做些什么。
基础
是时候创建我们的基本应用程序了。 使用命令行生成一个新的Play应用
play new jsRoutingScala
接受默认名称jsRoutingScala,然后为Scala应用程序选择选项。
_ __ | | __ _ _ _| |
| '_ \| |/ _' | || |_|
| __/|_|\____|\__ (_)
|_| |__/
play! 2.1.5 (using Java 1.7.0_17 and Scala 2.10.0), http://www.playframework.org
The new application will be created in /tmp/jsRoutingScala
What is the application name? [jsRoutingScala]
>
Which template do you want to use for this new application?
1 - Create a simple Scala application
2 - Create a simple Java application
> 1
OK, application jsRoutingScala is created.
Have fun!
这将为我们提供一个称为Application的基本控制器,并提供一些视图。 我们仍然需要创建一个模型类,所以现在就开始做。 在app目录中,创建一个名为models的包。 在模型包中,创建一个名为Person的类。 注意JSON读/写支持–这将允许控制器在Scala和JSON之间序列化和反序列化Person的实例。
package models
import anorm._
import anorm.SqlParser._
import play.api.Play.current
import play.api.db.DB
import play.api.libs.json._
import anorm.~
case class Person(id: Pk[Long] = NotAssigned, name: String)
object Person {
val simple = {
get[Pk[Long]]("person.id") ~
get[String]("person.name") map {
case id~name => Person(id, name)
}
}
def insert(person: Person) = {
DB.withConnection { implicit connection =>
SQL(
"""
insert into person values (
(select next value for person_seq),
{name}
)
"""
).on(
'name -> person.name
).executeInsert()
} match {
case Some(long) => long
case None => -1
}
}
def delete(id: Long) = {
DB.withConnection { implicit connection =>
SQL("delete from person where id = {id}").on('id -> id).executeUpdate()
}
}
def getAll: Seq[Person] = {
DB.withConnection { implicit connection =>
SQL("select * from person").as(Person.simple *)
}
}
implicit object PersonReads extends Reads[Person] {
def reads(json: JsValue): JsResult[Person] =
JsSuccess[Person](Person(NotAssigned, (json \ "name").as[String]), JsPath())
}
implicit object PersonWrites extends Writes[Person] {
def writes(person: Person) = Json.obj(
"id" -> Json.toJson(person.id.get),
"name" -> Json.toJson(person.name)
)
}
}
我们将需要一个数据库,因此编辑conf / application.conf并取消注释以下几行:
db.default.driver=org.h2.Driver
db.default.url="jdbc:h2:mem:play"
因为此示例使用Anorm,所以我们需要自己配置数据库。 在conf文件夹中,创建一个名为Evolutions的文件夹,其中包含另一个文件夹default 。 在此处创建一个名为1.sql的文件,并将以下内容复制到其中:
# --- !Ups
create table person (
id bigint not null,
name varchar(255),
constraint pk_person primary key (id))
;
create sequence person_seq;
# --- !Downs
SET REFERENTIAL_INTEGRITY FALSE;
drop table if exists person;
SET REFERENTIAL_INTEGRITY TRUE;
drop sequence if exists person_seq;
现在,我们准备构建控制器。
控制器
在控制器包中,打开“应用程序”对象。 已经有一个索引方法,但是我们不需要更改它。
该应用程序将允许我们创建,删除和获取Person实例,因此我们需要支持这些操作的方法。
def getAll() = Action {
Ok(Json.toJson(Person.getAll))
}
def delete(id: Long) = Action {
Person.delete(id)
Ok("Deleted " + id)
}
def create = Action { implicit request =>
request.body.asJson match {
case None => BadRequest
case Some(json: JsValue) => {
val person: Person = json.as[Person]
val id = Person.insert(person)
Ok(Json.obj(
"id" -> id,
"name" -> person.name
))
}
}
}
def jsRoutes = Action { implicit request =>
Ok(Routes.javascriptRouter("appRoutes")(
controllers.routes.javascript.Application.create,
controllers.routes.javascript.Application.delete,
controllers.routes.javascript.Application.getAll))
.as("text/javascript")
}
这些方法涵盖了我们的业务逻辑,因此我们可以将它们添加到路由文件中
GET /person controllers.Application.getAll()
DELETE /person/:id controllers.Application.delete(id: Long)
POST /person controllers.Application.create()
添加JS路由支持
到目前为止,如此正常。 我们拥有可以通过HTTP调用访问的业务逻辑,但是没有专门的JS支持。 我们需要向控制器添加另一个方法,该方法指定我们希望在JS路由对象中可用的路由。
// jsRoutes
此方法将生成一个可以加载到客户端JavaScript文件。 进入视图后,我们将看到此内容。 因为这是从客户端调用的,所以我们需要向路由添加另一个条目。 注意路由的位置非常重要–它必须在现有的“ / assets / * file”条目之前,否则该路由将占用请求。
GET /assets/js/routes controllers.Application.jsRoutes()
GET /assets/*file controllers.Assets.at(path="/public", file)
风景
现在我们要去哪里做。 第一步是使我们的视图了解JS路由代码,我们可以通过简单地将脚本标签添加到views / main.scala.html来做到这一点。
<script src="@controllers.routes.Application.jsRoutes()" type="text/javascript"></script>
我们终于准备好使用我们的JS路由对象。 以下代码是一个非常基本的单页应用程序,可连接到后端的获取,创建和删除功能。 将此代码复制并粘贴到现有的views / index.scala.html文件中,然后看一下它在做什么。 您可能会注意到此代码与Java示例中的代码相同。
@(message: String)
@main("Play 2 JavaScript Routing") {
<fieldset>
<form>
<label for="personName">Name:
<input type="text" name="personName" id="personName"/>
<input type="button" value="Create" id="createPerson"/>
</form>
</fieldset>
<ul id="peopleList">
<script>
var doc = $ (document);
doc.ready (function() {
// Delete a person
doc.on ('click', '.deletePerson', function(e) {
var target = $(e.target);
var id = target.data('id');
appRoutes.controllers.Application.delete(id).ajax( {
success : function ( data ) {
target.closest('li').remove();
}
});
});
// Create a new person
$('#createPerson').click(function() {
var personNameInput = $('#personName');
var personName = personNameInput.val();
if(personName && personName.length > 0) {
var data = {
'name' : personName
};
appRoutes.controllers.Application.create().ajax({
data : JSON.stringify(data),
contentType : 'application/json',
success : function (person) {
$('#peopleList').append('<li>' + person.name + ' <a href="#" data-id="' + person.id + '" class="deletePerson">Delete</a></li>');
personNameInput.val('');
}
});
}
});
// Load existing data from the server
appRoutes.controllers.Application.getAll().ajax({
success : function(data) {
var peopleList = $('#peopleList');
$(data).each(function(index, person) {
peopleList.append('<li>' + person.name + ' <a href="#" data-id="' + person.id + '" class="deletePerson">Delete</a></li>');
});
}
});
}) ;
</script>
}
这里有三行将生成对服务器的调用,即
- appRoutes.controllers.Application.delete(id).ajax
- appRoutes.controllers.Application.create()。ajax
- appRoutes.controllers.Application.getAll()。ajax
让我们以delete为例,因为它带有一个参数。
- appRoutes
- 看一下您的Application类,您将看到名称appRoutes被赋予JS路由器。
- 控制器应用
- 这是目标控制器的标准名称。
- 删除(ID)
- 方法调用,包括参数。
- 阿贾克斯
- 此功能将调用服务器。
摘要
这似乎是很多工作,但其中大部分是在创建项目。 实际的JS路由只占很小的时间投资,却能获得不错的回报。 改型现有代码以使用此方法非常容易,一旦您从中受益,继续采用这种风格也非常简单。
应用范例
- 您可以在GitHub上从本文中找到示例代码: https : //github.com/schaloner/jsRoutingScala
Java
本文从Scala的角度介绍了如何使用此功能。 在同级文章中,还演示了Java支持。 两者之间的差异很小,从客户端的角度来看不存在。 您可以在这里阅读本文。
翻译自: https://www.javacodegeeks.com/2014/01/javascript-routing-in-play-2-scala-edition.html