• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
Joanna Qian
Stay Hungry, Stay Foolish!
博客园    首页    新随笔    联系   管理    订阅  订阅
《Play for Java》学习笔记(五)Form

本书第六章以一个实例介绍了Play Framework中Form的使用,如何绑定数据,如何进行验证

一、项目结构和action

二、Play中表单的使用

1. 在Controller中使用Form——处理提交和验证

在play.data包中包含了处理HTTP表单数据提交和验证(HTTP form data submission and validation)的一些helpers,一般步骤是先定义一个play.data.Form并包裹进其所用模型类class,如下所示:

Form<User> userForm =Form.form(User.class);
//引入包
import play.data.*;
import static play.data.Form.*;
//
Model—— User Object public class User { public String email; public String password; } //controller—— userForm Form<User> userForm = Form.form(User.class); //1. 定义并包裹模型User类 //This form can generate a User result value from HashMap<String,String> data Map<String,String> anyData = new HashMap(); //2. 写入数据到HashMap--mocking data anyData.put("email", "bob@gmail.com"); anyData.put("password", "secret"); User user = userForm.bind(anyData).get(); //3. 写入数据到表单并绑定给User对象(保存数据) //If have a request available in the scope, bind directly from the request content User user = userForm.bindFromRequest().get();

1.1 在表单中显示预设好的数据——Displaying a form with preset values

public class Products extends Controller {
    private static final Form<Product> productForm = Form.form(Product.class);
    ...
    public static Result details(String ean) {
        final Product product = Product.findByEan(ean);
        if (product == null) {
            return notFound(String.format("Product %s does not exist.", ean));  //处理错误
        }
        Form<Product> filledForm = productForm.fill(product);        //填写product实例中的数据到页面表单productForm中
        return ok(details.render(filledForm));                       //返回(跳转)渲染页面
    }
    ...
}

route是

GET         /products/:ean               controllers.Products.details(ean: String)

1.2 处理表单的输入


① 创建boundForm对象,用于接受从HTTP传入的数据信息,HTTP --> boundForm
② boundForm将接受的表单数据传给Product的实例,boundForm --> product
③ 调用produce.save()添加表单数据到Product的实例

1.3 JavaForm(Controller)小结

① 定义表单类用于接收和发送数据  -->  Form<Product> productForm = Form.form(Product.class);
② 把模型中数据写入表单 --> Form<Product> filledForm = productForm.fill(product);
③ 把页面表单的数据写入模型 --> Form<Product> boundForm = productForm.bindFromRequest();
Product product = boundForm.get();
 
product.save();

2. 在模板中使用表单——Form template helpers

在模板中我们可以使用Form template的helpers和helper.twitterBootstrap来处理表单各个项,这些helper会自动生成相应的HTML代码,如:

@helper.form(action = routes.Products.save()) {
    @helper.inputText(productForm("ean"), '_label -> "EAN", '_help -> "Must be exaclty 13 numbers.")
}

会产生如下HTML代码(helper.twitterBootstrap)

<div class="clearfix  " id="ean_field">
    <label for="ean">EAN</label>
    <div class="input">        
        <input type="text" id="ean" name="ean" value="" >
        <span class="help-inline"></span>
        <span class="help-block">Must be exaclty 13 numbers.</span> 
    </div>
</div>

说明: 该helper可用的可选参数如下

'_label -> "Custom label"
'_id ->"idForTheTopDlElement"
'_help -> "Custom help" '_showConstraints ->false
'_error -> "Force an error" '_showErrors ->false

项目中details.scala.html的代码如下:

2.1 引入helper

  • @(productForm: Form[Product]) —— action传入的参数
  • @import helper._ —— Form helpers
  • @import helper.twitterBootstrap._ —— bootstrap helpers

2.2 生成<form> tag

@helper.form(action = routes.Products.save()) { ... }

可在生成的时候加入参数

@helper.form(action = routes.Products.save(),''id -> "form") { ... }

2.3 生成 <input> element

还可以自定义HTML输入

@helper.input(myForm("email")) { (id, name, value, args) =>
    <inputtype="date"name="@name"id="@id" @toHtmlArgs(args)>
}

 2.4 自定义的helper——生成自定义的myFieldConstructorTemplate

@(elements: helper.FieldElements)
<div class="@if(elements.hasErrors) {error}">
<label for="@elements.id">@elements.label</label>
<div class="input">
    @elements.input
    <span class="errors">@elements.errors.mkString(", ")</span>
    <span class="help">@elements.infos.mkString(", ")</span>
</div>
</div>

 保存为views/myFieldConstructorTemplate.scala.html文件,现在就可以使用这个自定义的FieldElements了

@implicitField = @{ FieldConstructor(myFieldConstructorTemplate.f) }
@inputText(myForm("email"))

 三、数据绑定

PLay中有三种绑定方式

● 表单绑定(Form binding),见前所述

● URL查询参数绑定(URL query parameters binding):

GET   /products         Products.edit(id: Long) -->  映射到URL:    http://localhost:9000/products?id=12

● URL路径绑定(URL path binding)

GET   /products/:id    Products.edit(id: Long)  -->  映射到URL:   http://localhost:9000/products/12

 1、 简单数据绑定

模型如下:

public class Product {
    public String ean;
    public String name;
    public String description;
}

路由如下:

GET   /products/save   controllers.Products.save()

controller中的部分代码如下:

//创建一个新的Product实例,用来接受HTTP数据
Form<models.Product> productForm = form(models.Product.class).bindFromRequest();
//将从表单得到的数据赋给Product实例
Product product = boundForm.get();
//调用Product的save方法,将数据添加到product实例中
product.save();
...
return redirect(routes.Products.list());

在浏览器中输入http://localhost:9000/product/save?ean=1111111111111&name=product&description=a%20description(该URL的参数即是在表单中填入的数据,通过GET方法传给URL)即可激活Product.save()方法,并转入Product.list()方法,显示从HTTP URL传入的数据。

 2、 复杂数据绑定

假定有两个类Product和Tag,其关系如图

//Tag Class
public class Tag {
    public Long id;
    public String name;
    public List<Product> products;
    public Tag(Long id, String name, Collection<Product> products) {
        this.id = id;
        this.name = name;
        this.products = new LinkedList<Product>(products);
        for (Product product : products) {
            product.tags.add(this);
        }
    }
   public static Tag findById(Long id) {
    for (Tag tag : tags) {
      if(tag.id == id) return tag;
    }
    return null;
   }
}
//Product Class
public class Product implements PathBindable<Product>, QueryStringBindable<Product> {
   public String ean;
  public String name;
  public String description;
  public Date date = new Date();
  public Date peremptionDate = new Date();
  public List<Tag> tags = new LinkedList<Tag>();

  public Product() {
  }

  public Product(String ean, String name, String description) {
    this.ean = ean;
    this.name = name;
    this.description = description;
  }
  public static List<Product> findAll() {
    return new ArrayList<Product>(products);
  }

  public static Product findByEan(String ean) {
    for (Product candidate : products) {
      if (candidate.ean.equals(ean)) {
        return candidate;
      }
    }
    return null;
  }

  public static List<Product> findByName(String term) {
    final List<Product> results = new ArrayList<Product>();
    for (Product candidate : products) {
      if (candidate.name.toLowerCase().contains(term.toLowerCase())) {
        results.add(candidate);
      }
    }
}
Tag + Product Class

l浏览器中显示效果如图:

        

在Products Controller中加入以下代码

public class Products extends Controller {
    ...
    public static Result save() {
       ... (binding and error handling)
        Product product = boundForm.get();
        List<Tag> tags = new ArrayList<Tag>();
        for (Tag tag : product.tags) {
          if (tag.id != null) {
            tags.add(Tag.findById(tag.id));
          }
        }
       product.tags = tags;
       product.save();
    ... (success message and redirect)
   }

在Tag模型类中加入模拟数据(mocking data)

static {
   //The lightweight tag is added to product names matching paperclips 1
    tags.add(new Tag(1L, "lightweight", Product.findByName("paperclips 1")));
   //The metal tag is added to all the products (they all match paperclips)
    tags.add(new Tag(2L, "metal", Product.findByName("paperclips")));
    //Theplastic tag is added to all the products (they all match paperclips)
    tags.add(new Tag(3L, "plastic", Product.findByName("paperclips")));
  }

在details.scala.html,加入对应于Tag的相关代码

<div class="control-group">
  <div class="controls">
    <input name="tags[0].id" value="1" type="checkbox"
    @for(i <- 0 to 2) {
      @if(productForm("tags[" + i + "].id").value=="1"){ checked }   //如果该模型的Tag为1(paperclips 1),将该选择项选中,即该产品据有lightweight属性
    }> lightweight
    <input name="tags[1].id" value="2" type="checkbox"               //如果该模型的Tag为2(paperclips),将该选择项选中,即该产品据有metal属性
    @for(i <- 0 to 2) {
      @if(productForm("tags[" + i + "].id").value=="2"){ checked }   //如果该模型的Tag为3(paperclips),将该选择项选中,即该产品据有plastic属性
    }> metal
    <input name="tags[2].id" value="3" type="checkbox"
    @for(i <- 0 to 2) {
      @if(productForm("tags[" + i + "].id").value=="3"){ checked }
    }> plastic
  </div>
</div>

 3. 自定义数据绑定

 3.1 绑定URL Path和模型对象

①将route从 GET    /product/:ean       controllers.Product.details(ean: String)     改为

               GET    /product/:ean       controllers.Product.details(ean: models.Product) 

②products/list.scala.html中的 <a href="@routes.Products.details(product.ean)">  改为
                   <a href="@routes.Products.details(product)">

③修改Product类

  • 继承PathBindable接口并重写bind、unbind和javascriptUnbind方法
  • bind():                URL       --> product
  • unbind():             product -->view
  • javascriptUnbind():    支持Javascript和Ajax call

④ 在controller Products中加入新action

该action用于自动绑定Product对象和URL路径中的ean

 3.2 绑定URL Path参数(Query string)和模型对象

原理同上,只是继承的接口是play.mvc.QueryStringBindable

①将route从 GET    /product/:ean       controllers.Product.details(ean: String)     改为

              GET    /product/           controllers.Product.details(ean: models.Product)  这样我们就可以使用像下面的URL了:  /products?ean=1

②products/list.scala.html中的 <a href="@routes.Products.details(product.ean)">  改为
                    <a href="@routes.Products.details(product)">

③修改Product类

④ 在Products Controller中加入新action(同上)

4、 HTTP URL复杂参数的格式化

  • 如果HTTP URL的参数是String、Integer、Long等简单类型,不用进行格式化直接使用,但如果是复杂类型(如日期Date类型)就必须将其映射为array或List对象,才能作为URL的参数使用。
  • Play提供了内置的格式化方法,均放在play.mvc.data.Formats包中
  • 例如对于Date,必须先对其进行格式化
    @Formats.DateTime(pattern = "yyyy-MM-dd")
    public Date date;
    然后才能作为URL的参数:   http://localhost:9000/product/save?date=2021-10-02

四、表单验证—— Validation

Play的验证仅和领域模型绑定,使用JSR-303和Hibernate的验证,通过为对象模型定义添加注解(annotation)来实现。

4.1 内置验证(build-in)

  在领域模型中使用注解(annotation)

 

  在Controller中使用Form对象的hasError方法来处理验证的错误意外

4.2 局部验证(partial)

 (以后补充)

4.3 自定义验证(custom)

  • ad hoc验证——该方法最简单、最快捷
  • 使用@ValidateWith并定义自己的Validator类
  • 定义行的JSR 303注解(annotation)和自己的Validator类

4.3.1 ad hoc验证——为每个领域模型类增加validate方法

4.3.2 使用Play的@ValidateWith并定义自己的Validator类

为每个领域模型类增加validate方法,以Product类的ean成员变量为例,为其添加自定义EanValidator类:

4.3.3 使用JSR-303注解并定义自己的Validator类

以Product类的ean成员变量为例

① 定义一个注解(annotation) —— 如: 自定义的Product类的成员变量ean的JSR-303类型注解EAN (Custom JSR-303 EAN annotation)


② 定义自定义EanValidator类——Custom JSR-303 validator

③在领域模型类中使用自定义的注解(annotation)

五、补充

本人比较有印象和感兴趣的是该实例中页面中删除记录的代码,居然直接使用javascript中的$.Ajax方法来实现删除,其全部代码如下所示:

list.scala.html

@(products: List[Product])
@main("Products catalogue") {
  <h2>All products</h2>
    <script>
     function del(urlToDelete) {
        $.ajax({
          url: urlToDelete,
          type: 'DELETE',
          success: function(results) {location.reload(); // Refresh the page   }
        });
      }
   </script>
   <table class="table table-striped">
    <thead><tr><th>EAN</th><th>Name</th><th>Description</th><th>Date</th><th></th></tr></thead>
    <tbody>
    @for(product <- products) {
      <tr>
        <td><a href="@routes.Products.details(product)">
          @product.ean 
        </a></td>
        <td><a href="@routes.Products.details(product)">@product.name</a></td>
        <td><a href="@routes.Products.details(product)">@product.name</a></td>
        <td>@product.date.format("dd-MM-yyyy")</td>
        <td>
          <a href="@routes.Products.details(product)"><i class="icon icon-pencil"></i></a> 
          <a onclick="javascript:del('@routes.Products.delete(product.ean)')"><i class="icon icon-trash"></i></a> 
        </td>
      </tr>
      }
  
    </tbody>
   </table>    
  <a href="@routes.Products.newProduct()" class="btn">
    <i class="icon-plus"></i> New product</a>
}

delete()方法如下

public static Result delete(String ean) {
    final Product product = Product.findByEan(ean);
    if(product == null) {
        return notFound(String.format("Product %s does not exists.", ean));
    }
    Product.remove(product);
    return redirect(routes.Products.list(1));
}

 参考:

  • http://www.playframework.com/documentation/2.2.x/JavaFormHelpers
  • http://www.playframework.com/documentation/2.2.x/JavaForms
posted on 2014-04-02 03:14  Joanna Qian  阅读(1142)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3