跳转到内容

使用 Click 框架进行 Java Web 应用程序开发/简介

来自维基教科书,开放的世界,开放的书籍

Click 是一个适用于商业 Java 开发人员的简单 JEE Web 应用程序框架。youtube Click 是一个开源项目,使用 Apache 许可证授权。

Click 使用基于事件的编程模型来处理 Servlet 请求,并使用 Velocity 来渲染响应。

该框架使用一个名为ClickServlet的单个 Servlet 充当请求调度器。当请求到达时ClickServlet创建一个Page对象来处理请求,然后使用页面的 Velocity 模板来渲染结果。

页面提供了一个简单的线程安全编程环境,每个 Servlet 请求都会创建一个新的页面实例。

可能是了解 Click 工作原理的最佳方法是直接深入研究一些示例

Hello World 示例

[编辑 | 编辑源代码]

Click 中的 Hello World 示例看起来像这样。

首先,我们将有一个HelloWorld页面类

package examples.page;
 
import java.util.Date;
import net.sf.click.Page;

public HelloWorld extends Page 
{
    public Date time = new Date();
}

接下来我们将有一个页面模板hello-world.htm:

<html>
  <body>
    <h2>Hello World</h2>
    Hello world from Click at $time
  </body>
</html>

最后,我们有一个click.xml配置文件,它告诉我们的 Click 应用程序将hello-world.htm请求映射到我们的HelloWorld页面类。

<click-app>
  <pages package="examples.page"/>
</click-app>

在运行时,ClickSerlvet 将 GET hello-world.htm 请求映射到我们的页面类example.page.HelloWorld并创建一个新实例。HelloWorld 页面创建一个新的公共Date对象,该对象使用字段名称自动添加到页面的模型中time.

然后将页面模型与模板合并,该模板将$time参数替换为Date对象。然后 Velocity 渲染合并后的模板,该模板看起来像

package examples.page;
 
import java.util.Date;
import net.sf.click.Page;

public HelloWorld extends Page 
{
    public Date time = new Date();
}

控件侦听器示例

[编辑 | 编辑源代码]

Click 包含一个库(控件),它提供用户界面功能。

最常用的控件之一是 ActionLink,您可以使用它让 HTML 链接调用页面对象上的方法。例如

public class ControlListenerPage extends Page 
{
   public ActionLink myLink = new ActionLink();
   public String msg;

   // ----------------------------------------------------------- Constructors

   /**
    * Create a new Page instance.
    */
   public ControlListenerPage() 
   {
       myLink.setListener(this, "onMyLinkClick");
   }

   // --------------------------------------------------------- Event Handlers

   /**
    * Handle the myLink control click event.
    */
   public boolean onMyLinkClick() 
   {
       msg = "ControlListenerPage#"   hashCode()
             " object method <tt>onMyLinkClick()</tt> invoked.";
 
        return true;
   }
}

在页面类中,我们创建一个名为myLink的 ActionLink,并将控件的侦听器定义为页面方法onMyLinkClick()。当用户点击 myLink 控件时,它将调用侦听器方法onMyLinkClick().

在 Click 中,控件侦听器方法可以具有任何名称,但必须返回一个布尔值。布尔返回值指定是否应继续处理页面事件。这种控件侦听器模式提供了一种简便的方法来连接操作侦听器方法,而无需定义匿名内部类。

回到我们的示例,在页面模板中,我们定义了一个 HTML 链接,并让myLink控件渲染链接的 href 属性

<html>
  <head>
    <link type="text/css" rel="stylesheet" href="style.css"></link>
  </head>
  <body>

  Click myLink control <a href="$myLink.href">here</a>.

  #if ($msg)
    <div id="msgDiv"> $msg </div>
  #end

  </body>
</html>

在运行时,此页面将渲染为

点击 myLink 控件 这里

当用户点击链接时,onMyLinkClick()方法被调用。然后该方法创建msg模型值,它在页面中渲染为

点击 myLink 控件 这里
ControlListenerPage#12767107 对象方法onMyLinkClick()被调用。

简单 Table 示例

[编辑 | 编辑源代码]

Click 中最实用的控件之一是Table控件。

下面提供了一个在客户的Table中使用Page控件的示例用法

public class SimpleTablePage extends Page 
{
    public Table table = new Table();

    // ------------------------------------------------------------ Constructor

    public SimpleTablePage() 
    {
        table.setClass(Table.CLASS_ITS);

        table.addColumn(new Column("id"));
        table.addColumn(new Column("name"));
        table.addColumn(new Column("email"));
        table.addColumn(new Column("investments"));
    }

    // --------------------------------------------------------- Event Handlers

    /**
     * @see Page#onRender()
     */
    public void onRender() 
    {
    	List list = getCustomerService().getCustomersSortedByName(10);
    	table.setRowList(list);
    }
}

在此页面代码示例中,声明了一个 Table 控件,我们设置了表的 HTML 类,然后定义了表的数量Column对象。在列定义中,我们在构造函数中指定了列的名称,该名称用于表列标题,以及指定要渲染的行对象属性。

我们需要做的最后一件事是用数据填充表格。为此,我们覆盖了 PageonRender()方法并在渲染前设置表格行列表。

在我们的页面模板中,我们只需引用$table对象,该对象在调用其toString()方法时被渲染。

<html>
<head>
$cssImports
</head>
<body>

$table

</body>
</html>
$jsImports

请注意,我们在上面还指定了 $cssImports 引用,以便表格可以在标题中包含任何 CSS 导入或样式,以及 $jsImports 引用,以便在底部包含任何 JavaScript 导入或脚本。

在运行时,Table 将在页面中渲染为

高级 Table 示例

[编辑 | 编辑源代码]

Table 控件还提供对

  • 自动渲染
  • 列格式化和自定义渲染
  • 自动分页
  • 链接控件支持

下面提供了一个更高级的 Table 示例

public class CustomerPage extends Page 
{
    public Table table = new Table();
    public PageLink editLink = new PageLink("Edit", EditCustomer.class);
    public ActionLink deleteLink = new ActionLink("Delete", this, "onDeleteClick");

    // ------------------------------------- Constructor

    public CustomersPage() 
    {
        table.setClass(Table.CLASS_ITS);
        table.setPageSize(10);
        table.setShowBanner(true);
        table.setSortable(true);

        table.addColumn(new Column("id"));

        table.addColumn(new Column("name"));

        Column column = new Column("email");
        column.setAutolink(true);
        column.setTitleProperty("name");
        table.addColumn(column);

        table.addColumn(new Column("investments"));

        editLink.setImageSrc("/images/window-edit.png");
        editLink.setTitle("Edit customer details");
        editLink.setParameter("referrer", "/introduction/advanced-table.htm");

        deleteLink.setImageSrc("/images/window-delete.png");
        deleteLink.setTitle("Delete customer record");
        deleteLink.setAttribute("onclick", "return window.confirm('Are you sure you want to delete this record?');");

        column = new Column("Action");
        column.setTextAlign("center");
        AbstractLink[] links = new AbstractLink[] { editLink, deleteLink };
        column.setDecorator(new LinkDecorator(table, links, "id"));
        column.setSortable(false);
        table.addColumn(column);
    }

    // ---------------------------------- Event Handlers

    /**
     * Handle the delete row click event.
     */
    public boolean onDeleteClick() 
    {
        Integer id = deleteLink.getValueInteger();
        getCustomerService().deleteCustomer(id);
        return true;
     }
 
     /**
     * @see Page#onRender()
     */
    public void onRender() 
    {
    	List list = getCustomerService().getCustomersByName();
    	table.setRowList(list);
    }
}

在这个Page代码示例中,声明了一个Table控件,并添加了一些Column对象。deleteLink ActionLink 控件用作“操作”列的装饰器。当点击此控件时,它将调用 PageonDeleteClick()方法。最后,我们有 PageonRender()方法,该方法用于在渲染前用行填充 Table 控件。

在我们的页面模板中,我们只需引用 $table 对象,该对象在调用其toString()方法时被渲染。

<html>
<head>
$cssImports
</head>
<body>

$table

</body>
</html>
$jsImports

在运行时,Table 将在页面中渲染为

在此示例中,如果用户点击“删除”链接,onDeleteClick()方法将被调用,在页面上删除客户记录。

简单 Form 示例

[编辑 | 编辑源代码]

TheFormandField控件也是 Click 框架中最常用的控件之一。

下面的 SimpleForm 页面演示了如何使用这些控件。

在我们的示例代码中,页面的构造函数添加了一个TextField字段和一个Submit按钮到表单。页面方法也设置为表单上的控件侦听器。请注意,在此示例中,页面的公共form字段会自动添加到其控件列表中。

public class SimpleForm extends Page 
{
    public Form form = new Form();
    public String msg;

    // ------------------------------------------------------------ Constructor

    public SimpleForm() 
    {
        form.add(new TextField("name", true));
        form.add(new Submit("OK"));

        form.setListener(this, "onSubmit");
    }

    // --------------------------------------------------------- Event Handlers

    /**
     * Handle the form submit event.
     */
    public boolean onSubmit() 
    {
        if (form.isValid()) 
        {
            msg = "Your name is "   form.getFieldValue("name");
        }
        return true;
     }
 }

接下来,我们有 SimpleForm 模板 simple-form.htm。Click 应用程序会自动将simple-form.htm模板与SimpleForm类关联。

<html>
<head>
$cssImports
</head>
<body>

$form

#if ($msg)
  <div id="msgDiv"> $msg </div>
#end

</body>
</html>
$jsImports

第一次请求 SimpleForm 页面时,$form 对象会自动将其自身渲染为

姓名*

现在,如果用户没有输入他们的姓名,然后点击“确定”按钮提交表单。ClickServlet 会创建一个新的 SimpleForm 页面,并处理表单控件。

表单控件处理其字段,并确定它无效。然后表单调用侦听器方法onSubmit()方法。由于表单无效,此方法只需返回 true,表单会渲染字段验证错误。

[# 必须为姓名输入值]
姓名*

请注意,表单会在发布和验证循环中自动维护已输入的状态。

现在,如果用户输入了他们的姓名,然后点击“确定”按钮,表单将变为有效,并且onSubmit()在页面的模型中添加一个 msg。它将渲染为

姓名*
您的姓名是 John Masters

高级 Form 示例

[编辑 | 编辑源代码]

facebook 下面的AdvancedForm页面提供了一个更高级的演示,演示了如何使用 Form、Field 和 FielsSet 控件。

首先,我们有一个AdvancedForm类,它在构造函数中设置了一个Form。表单的投资Select列表在页面的onInit()方法中填充。此时,任何页面依赖项(例如 CustomerService)都应该可用。

请注意,在此示例中,页面的公共form字段会自动添加到其控件列表中。该msg字段被添加到页面的模型中。

public class AdvancedForm extends Page 
{
    public Form form = new Form();
    public String msg;

    private Select investmentSelect = new Select("investment");

    // ------------------------------------------------------------ Constructor

    public AdvancedForm() 
    {
        FieldSet fieldSet = new FieldSet("Customer");
        form.add(fieldSet);

        TextField nameField = new TextField("name", true);
        nameField.setMinLength(5);
        nameField.setFocus(true);
        fieldSet.add(nameField);

        fieldSet.add(new EmailField("email", true));

        fieldSet.add(investmentSelect);

        fieldSet.add(new DateField("dateJoined", true));
        fieldSet.add(new Checkbox("active"));

        form.add(new Submit("ok", " OK ", this, "onOkClicked"));
        form.add(new Submit("cancel", this, "onCancelClicked"));
    }

    // --------------------------------------------------------- Event Handlers

    /**
     * @see Page#onInit()
     */
    public void onInit() 
    {
        CustomerService customerService = getCustomerService();
        investmentSelect.add(Option.EMPTY_OPTION);
        investmentSelect.addAll(customerService.getInvestmentCatetories());
    }

    /**
     * Handle the OK button click event.
     *
     * @return true
     */
    public boolean onOkClicked() 
    {
        if (form.isValid()) 
        {
            Customer customer = new Customer();
            form.copyTo(customer);

            getCustomerService().saveCustomer(customer);

            form.clearValues();

            msg = "A new customer record has been created.";
         }
         return true;
     }
 
     /**
     * Handle the Cancel button click event.
     *
     * @return false
     */
    public boolean onCancelClicked() 
    {
        setRedirect(HomePage.class);
        return false;
     }
 }

接下来,我们有 AdvancedForm 模板 advanced-form.htm。Click 应用程序会自动将advanced-form.htm模板与AdvancedForm类关联。

<html>
<head>
$cssImports
</head>
<body>

#if ($msg)
  <div id="msgDiv"> $msg </div>
#end

$form

</body>
</html>
$jsImports

第一次请求 AdvancedForm 页面时,$form 对象会自动将其自身渲染为

客户
姓名*
电子邮件*
投资 债券商业房产选项住宅房产股票
加入日期* 日历
活跃

在此示例中,当用户点击“确定”按钮时,onOkClicked()方法被调用。如果表单有效,则会创建一个新的客户对象,并将表单字段值使用 FormcopyTo()方法复制到新对象中。然后保存客户对象,清除表单的字段值,并向用户显示一条信息消息。

如果用户点击“取消”按钮,则请求将重定向到应用程序的 HopePage。

表单布局

[编辑 | 编辑源代码]

在本例中,我们使用表单控件来自动渲染表单和字段的 HTML。对于快速构建屏幕来说,这是一个很棒的功能,并且表单控件提供了一些布局选项。

为了进行细粒度的页面设计,您可以在页面模板中专门布局表单和字段。

华夏公益教科书