本教程中,我們會涉及到 Tapestry forms 的一些基礎知識,然後再通過一個 Tapestry 應用來加深理解。我們會看看如何在頁面之間傳遞信息。

這份教程的主題是一個用於追蹤 Tapestry 的第三方程序庫的應用程序。每個項目都有這些屬性:

  • name
  • release ID
  • short and long description
  • category
  • supported Tapestry version
  • release date
  • public (visible to others) or private (visible only to the owner)

稍後的教程將會回到這個主題上,還會談到關於數據庫存取的問題。在本教程中,我們會將上述的數據集合(在 AddProject 的頁面中)然後顯示出來。

Home Page

我們應用程序的主頁非常簡單,並不需要 Java 類:

<html jwcid="@Shell" title="Tapestry Component Database"><body><h1>Tapestry Component Database</h1>

<p>  Options:</p>

<ul>  <li><a jwcid="@PageLink" page="AddProject">Add New Project</a></li></ul>

</body></html>

我們介紹另一個很好用的組件:Shell。這個組件是為了方便生成 <html>、<head> 以及 <title> 標籤(雖然可生成的標籤不多)。

Value Object

Tapestry 其中一個基石就是直接編輯 value 對象屬性的能力。在一個真正的應用程序中,這些 value 對象從數據庫取出,由 Tapestry 組件編輯,然後返回到數據庫中。

與頁面不同,這種由 Tapestry 編輯對象的方法並不需要什麼條件,它們不必繼承某個基類或是實現某個接口。它們是 MVC 模式中真正的 Model。

在本教程中,我們會用到一個非常簡單的對象:

package tutorial.forms.data;

import java.util.Date;

/** * Contains the name and description of a release of a project. * * @author Howard M. Lewis Ship */public class ProjectRelease {

    private String name;    private String releaseId;    private String shortDescription;    private String longDescription;    private String category;    private String tapestryVersion;    private Date releaseDate;    private boolean public;

    /**     * A user-specified category, used to group similar projects.     */    public String getCategory() {        return this.category;    }

    public void setCategory(String category) {        this.category = category;    }

    /**     * A longer description used on a detail page.     */    public String getLongDescription() {        return this.longDescription;    }

    public void setLongDescription(String longDescription) {        this.longDescription = longDescription;    }

    /**     * The name of the project.     */    public String getName() {        return this.name;    }

    public void setName(String name) {        this.name = name;    }

    /**     * If true, the project is visible to other users. If false, then the project is not visible.     * This is used as a "draft" mode, when information about the project is not complete.     */    public boolean isPublic() {        return this.public;    }

    public void setPublic(boolean public) {        this.public = public;    }

    /**     * The date when the project was released. Used to generate a chronological listing.     */    public Date getReleaseDate() {        return this.releaseDate;    }

    public void setReleaseDate(Date releaseDate) {        this.releaseDate = releaseDate;    }

    /**     * The version number of the project that was released.     */    public String getReleaseId() {        return this.releaseId;    }

    public void setReleaseId(String releaseId) {        this.releaseId = releaseId;    }

    /**     * A single-line description used in an overview listing.     */    public String getShortDescription() {        return this.shortDescription;    }

    public void setShortDescription(String shortDescription) {        this.shortDescription = shortDescription;    }

    /**     * The version of Tapestry required for the project.     */    public String getTapestryVersion() {        return this.tapestryVersion;    }

    public void setTapestryVersion(String tapestryVersion) {        this.tapestryVersion = tapestryVersion;    }}

AddProject Page

正如我們所見,主頁包含了一個 AddProject 頁面的鏈接;AddProject 頁面包含了一個收集項目信息的表單(這些信息會提交數據庫存儲)。在這個例子裡沒有數據庫,但我們還是可以收集這些信息。

HTML Template

<html jwcid="@Shell" title="Add New Project">  <body jwcid="@Body">    <h1>Add New Project</h1>    <form jwcid="form@Form" success="listener:doSubmit">      <table>        <tr>          <th>Name</th>          <td>            <input jwcid="name@TextField" value="ognl:project.name" size="40"/>          </td>        </tr>        <tr>          <th>Release ID</th>          <td>            <input jwcid="release@TextField" value="ognl:project.releaseId" size="20"/>          </td>        </tr>        <tr>          <th>Short Description</th>          <td>            <input jwcid="short@TextField" value="ognl:project.shortDescription" size="40"/>          </td>        </tr>        <tr>          <th>Long Description</th>          <td>            <textarea jwcid="long@TextArea" value="ognl:project.longDescription" rows="10" cols="40"/>          </td>        </tr>        <tr>          <th>Tapestry Version</th>          <td>            <input jwcid="tapestryVersion@TextField" value="ognl:project.tapestryVersion" size="20"/>          </td>        </tr>        <tr>          <th>Release Date</th>          <td>            <input jwcid="releaseDate@DatePicker" value="ognl:project.releaseDate"/>          </td>        </tr>        <tr>          <th>Public</th>          <td>            <input jwcid="public@Checkbox" value="ognl:project.public"/>          </td>        </tr>      </table>      <input type="submit" value="Add Project"/>    </form>  </body></html>

這個模板引入了一些新的組件:

  • Body -- 生成頁面的 JavaScript(需要使用 DatePicker)
  • Form -- 生成 HTML 表單並控制 submit 的行為
  • TextField -- 創建用於編輯(讀取及更新)屬性的 text field(<input type="text" />)
  • TextArea -- 同 TextField,但生成的是多行的 <textarea>
  • DatePicker -- 彈出式的 JavaScript 日曆
  • Checkbox -- 編輯頁面的 boolean 屬性

表單中的 success 參數連接到監聽器函數上。只有在驗證通過後才會調用監聽器。我們會在稍後的教程中討論到信息的驗證。

Body 組件在 Tapestry 中扮演著一個重要的角色;它在頁面輸出時負責組織所有的 JavaScript。它幫助組件為客戶端的變量和函數生成唯一的名稱,將所有組件在頁面中生成的 JavaScript 分成兩大塊(一塊在頁面的頂部,一個在底部)。DatePicker 組件在由 Body 組件封裝之前不會進行工作。

TextField 和 TextArea 用於編輯頁面的屬性。因為 value 參數是 OGNL 表達式,所以編輯由頁面類直接暴露的屬性並不是限制,這樣可以跟蹤屬性路徑。我們馬上會看到怎樣定義項目頁面的屬性。

正如你所見,Tapestry 提供了一些用於編輯指定屬性類型的組件。另外,我們還會談談如何配置一個既有的組件讓它可以編輯其它的類型。

AddProject Page Class

我們從一個最小的類開始,然後在必要時加入一些細節。

package tutorial.forms.pages;

import org.apache.tapestry.html.BasePage;import tutorial.forms.data.ProjectRelease;

/** * Java class for the AddProject page; contains * a form used to collect data for creating a new * {@link tutorial.forms.data.ProjectRelease}. * * @author Howard M. Lewis Ship */public abstract class AddProject extends BasePage {

    public abstract ProjectRelease getProject();

    public void doSubmit() {    }}

或許這個類實在是太小了;如果我們運行程序然後點擊 Add New Project 鏈接會出現一個異常:

這個異常源於一個空值:我們定義了存儲 ProjectRelease 對象的地方但並實際上沒有提供它的實例。OGNL 企圖解除對這個空值的引用然後擲出 OgnlException。這裡我們可以看到 Tapestry 異常報告的好處:它顯示的堆棧異常信息給出了應用程序的 context(模板中出錯的行)而沒有晦澀難懂的信息。

我們需要做的是創建 ProjectRelease 的實例然後將它存儲在屬性中,好讓 TextField 組件可以編輯它。我們得非常小心因為在一個運行的應用中,頁面會被置於緩衝池中然後被不斷的複用。

對於這種情況,正確的做法是監聽 PageBeginRender 事件,接著將新的實例存儲到屬性中。ProjectRelease 對象會在 request 期間使用,然後於 request 的最後丟棄。

監聽這些生存期事件非常簡單;你只需要選擇一個適當的 listener 接口然後實現它;Tapestry 就會自動為你的頁面進行註冊以接收通知。本例的接口是 PageBeginRenderListener:

package tutorial.forms.pages;

import java.util.Date;

import org.apache.tapestry.event.PageBeginRenderListener;import org.apache.tapestry.event.PageEvent;import org.apache.tapestry.html.BasePage;

import tutorial.forms.data.ProjectRelease;

/** * Java class for the AddProject page; contains a form used to collect data for creating a new * {@link tutorial.forms.data.ProjectRelease}. * * @author Howard M. Lewis Ship */public abstract class AddProject extends BasePage implements PageBeginRenderListener {

    public abstract ProjectRelease getProject();

    public abstract void setProject(ProjectRelease project);

    public void pageBeginRender(PageEvent event) {        ProjectRelease project = new ProjectRelease();        project.setReleaseDate(new Date());        setProject(project);    }

    public void doSubmit() {    }}

每當頁面輸出時,pageBeginRender() 函數就會被調用。同樣的,當頁面中的表單提交時調用函數,這得歸功於 Tapestry 中一個有用的小功能。我們不僅可以創建實例,而且還可以為數據域設值。

有了這個類,頁面就可以輸出了,然後我們可以輸入一些數據:

Form Submission

上面的實現中,提交表單似乎並沒有做些什麼。當然,表單提交,然後信息就從 request 中取出然後存儲到 ProjectRelease 對象的屬性中,但接著 AddProject 頁面就被重新輸出了。

那麼,怎樣保留 ProjectRelease 對象全得當表單提交、TextField 組件更新 project.name 屬性時我們不會再出現 NullPoinerException 呢?當 ProjectRelease 對象被丟棄時都發生了什麼?但當頁面的表單被提交時,PageBeginRender 接口就又會被觸發,就像一個表單輸出器一樣。這意味著既有的代碼確實創建了一個新的 ProjectRelease 實例,使得 TextField 可以存儲來自於表單的值。

同樣棒的是新的 ProjectRelease 對象更新了,我們需要的是發生些什麼。我們將要做出一些更動好讓表單提交時顯示不同的頁面。進一步的,表單會顯示由 AddProject 頁面收集而來的同樣的信息。

要完全這個,我們需要改動 doSubmit() 函數,讓它獲得 ShowProject 頁面。讓不同的頁面協同工作最簡單的辦法就是將一個頁面注入另一個頁面中。這可以用 annotation 來完成:

    @InjectPage("ShowProject")    public abstract ShowProject getShowProject(); 

這段 AddProject.java 中的代碼,建立了頁面 AddProject 與 ShowProject 之間的連接。

我們可以用 doSubmit() 中的代碼將信息從頁面 AddProject 傳遞到 ShowProject;也可以激活 ShowProject 頁面讓它作出響應。

public IPage doSubmit() {    ShowProject showProject = getShowProject();    showProject.setProject(getProject());    return showProject;} 

這小段代碼中有許多值得注意的地方。第一,函數不再是 void,它返回一個頁面(IPage 是所有頁面類都要實現的接口)。當 listener 函數返回一個頁面時,該頁面就成為了回應客戶端的有效頁面。

當我們談到對象、函數和屬性時,指得就是這個例子。我們沒有說到“ShowProject.html& amp; rdquo;模板,我們說的是 ShowProject 頁面,並沒有談到它的模板所在及模板裡都有些什麼。進一步的,要將信息從頁面 AddProject 傳遞到 ShowProject 上,我們並不需要和 HttpServletRequest 屬性攪和:我們將對象存儲在 ShowProject 頁面的屬性中。

萬事具備,我們現在可以提交表單然後看看 ShowProject 頁面了:

Annotation

转载于:https://www.cnblogs.com/jim/archive/2005/12/23/303589.html

【ProjectT】Tapestry • Quick Start • Forms相关推荐

  1. 【Spark】Spark Quick Start(快速入门翻译)

    本文主要是翻译Spark官网Quick Start.只能保证大概意思,尽量保证细节.英文水平有限,如果有错误的地方请指正,轻喷 目录导航在右上角,感谢两个大佬(孤傲苍狼  JavaScript自动生成 ...

  2. 【译】A quick list of new enterprise features in iOS 13, iPadOS, and macOS 10.15 Catalina

    苹果WWDC 2019主题演讲就在几个小时前结束,事实证明,正如我们希望的那样,企业有很多新闻,特别是关于身份管理和BYOD的新闻! 现在,这是我们的企业功能初步列表.可能会有更新-我们仍然必须等待完 ...

  3. 【swift】swift quick start

    一.常量和变量 常量let,变量var 也可以用于确定数组和字典的不可变和可变 二.数据类型: Int:整数类型,可表示有符号整数或无符号整数,分别使用Int和UInt表示. Float:单精度浮点数 ...

  4. 【CV】54篇最新CV领域综述论文速递!涵盖14个方向:目标检测/图像分割/医学影像/人脸识别等方向...

    文章来源于极市平台,作者CV开发者都爱看的 [导读]本文共汇总了从2020年4月至今的计算机视觉领域综述性论文,共54篇,涵盖图像分割. 图像识别.人脸识别/检测.医学影像.目标检测.3D方向(自动驾 ...

  5. 【笔记】三张图读懂机器学习:基本概念、五大流派与九种常见算法

    文章目录 [笔记]三张图读懂机器学习:基本概念.五大流派与九种常见算法 Chapter 1: A look at Machine learning 1.What is it? 2.How does m ...

  6. 机器学习数据集【转】

    500款各领域机器学习数据集,总有一个是你要找的 金融 美国劳工部统计局官方发布数据:http://dataju.cn/Dataju/web/datasetInstanceDetail/139 沪深股 ...

  7. 【ChatGPT】吴恩达『提示工程』课程完全笔记下载

    版权说明:『ChatGPT Prompt Engineering for Developers』是DeepLearning.AI出品的免费课程,版权属于DeepLearning.AI(https:// ...

  8. 【转】Visual C#创建和使用ActiveX组件

    开发基于.net平台上的程序员是很难从本质上把Visual C#和ActiveX组件联起来,虽然在使用Visual C#开发应用程序时,有时为了快速开发或者由于.Net Framework SDK的不 ...

  9. 【camera】YOLOV7实现实例分割+目标检测任务(训练、测试、量化、部署)

    [camera]YOLOV7实现实例分割+目标检测任务 代码下载地址 训练.测试.量化.部署代码 训练 For training, quite simple, same as detectron2: ...

  10. winform模拟登陆网页_【教程】模拟登陆网站 之 C#版(内含两种版本的完整的可运行的代码)...

    之前已经介绍过了网络相关的一些基础知识了: 以及简单的网页内容抓取,用C#是如何实现的: 现在接着来介绍,以模拟登陆百度首页: 为例,说明如何通过C#模拟登陆网站. 不过,此处需要介绍一下此文前提: ...

最新文章

  1. AlexNet 网络详解及Tensorflow实现源码
  2. 开发者都想收藏的深度学习脑图,我们抢先曝光了!
  3. React Native小白入门学习路径——五
  4. C语言中不安全的函数
  5. 6月17 表单验证
  6. 树莓派+百度api实现人脸识别
  7. 网络编程懒人入门(二):快速理解网络通信协议(下篇)
  8. Struts DispatchAction
  9. ubuntun系统mysql数据库同步_Ubuntu下MySQL主从同步配置步骤
  10. 机器学习 - 损失计算-softmax_cross_entropy_with_logits
  11. Vmware虚拟机不能使用键盘的解决方法
  12. 关于Excel的查询,可以通过格式查询(比如查找指定颜色的单元格)。
  13. 图像分辨率测试ISO12233 - 2017中文翻译
  14. Silverlight:针式打印机文字模糊的改善办法
  15. webpack打包、js处理兼容性、代码压缩问题Uncaught SyntaxError: Cannot use import statement outside a module (at index
  16. 前端涨薪必读,node.js入门保姆级教程
  17. 线下迁移线上,如何使用企业微信打造数字化企业?
  18. 【VB】机房收费系统(结账)
  19. 彻底颠覆几句话vm_彻底颠覆Web开发:面向移动的设计
  20. 常用流媒体协议(HLS/HTTP/RTP组播/RTSP)提取流的方法

热门文章

  1. 【ACL20】让笨重的BERT问答匹配模型变快!
  2. 摸鱼一年半,我终于摸出了一篇顶会论文
  3. 初学者|一文读懂命名实体识别
  4. word2vec理论与实践
  5. Kubeflow:连接云计算和机器学习的“桥梁”
  6. 在ASP.NET中清除页面状态
  7. python中计算DataFrame,Series的数据频率
  8. 常微分方程:初值问题与边值问题
  9. iis中间件_.NET Core技术研究中间件的由来和使用
  10. table列最小宽度 vue_Vue组件设计 - 先别管view