增强的事件机制
2009-10-30 由 十三 发表   评论(2条)   有3132人浏览

1. 前言

众所周知,JSF的事件机制借鉴了AWT和Swing的事件模型。熟悉AWT或Swing的同学知道,AWT和Swing的事件模型有一个很好的设计:为不同的事件侦听方法提供了不同的事件上下文,如对于鼠标事件侦听器的对应方法,有

public void mouseEntered(MouseEvent e)  {
}
public void mouseExited(MouseEvent e) {
}

用户可以从MouseEvent中方便得获取上下文信息,其他动作事件键盘事件也有相应的事件上下文。而在OperaMasks SDK中,通过 ajax:action 构件,同样支持鼠标动作键盘动作等等调用后台方法,那么,OperaMasks SDK的事件机制是否能做到像AWT和Swing那样提供丰富的事件上下文呢?事实上,在2.3版本中,OperaMasks SDK不仅做到了,而且可以说在一些方面做得比AWT和Swing更好,或者说,更适合Web程序。

2. 新特性

OperaMasks 2.3版本定义了一个接口 AjaxActionSupport,如果触发事件的构件实现了 AjaxActionSupport 接口,那么该构件的事件处理将有一些新特性。2.3 版本中实现了 AjaxActionSupport 接口的构件有

  • field 相关构件,有 w:textField, w:checkBox, w:calcNumberField, w:numberField, w:textArea, w:combo, w:timeField, w:dateField, w:dateTimeField 这些构件

  • grid 构件,有 w:dataGrid 构件和 w:editDataGrid 构件

  • w:tree 构件

下面是上述构件事件处理的新特性

2.1. 丰富的事件上下文

OperaMasks 2.3 新增了事件上下文支持。举个例子:在2.3之前的版本中,如果要响应 w:tree 的 onclick 事件并获取点击的节点的文本内容,页面如下

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE HTML PUBLIC "" "">
<f:view xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core"
	xmlns:w="http://www.apusic.com/jsf/widget" xmlns:layout="http://www.apusic.com/jsf/layout"
	xmlns:h="http://java.sun.com/jsf/html" xmlns:ajax="http://www.apusic.com/jsf/ajax"
	renderKitId="AJAX">
	<w:head>
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
	</w:head>
	<w:page title="Insert title here">
		<w:tree id="tree">
			<w:treeNode text="root"></w:treeNode>
		</w:tree>
	</w:page>
</f:view>

对应的 LiteBean 需要这么写

	@Bind
	UITree tree;1
	
	@Action
	public void tree_onclick() {
		System.out.println(tree.getEventNode().getText());2
	}
1

绑定 w:tree,只为 onclick 响应方法获取触发事件节点

2

从绑定的 UITree 中获取触发事件的节点

现在后台LiteBean可以这么写

@Action
public void tree_onclick(TreeNodeEvent event) {
    System.out.println(event.getEventNode().getText()); 1
}
1

直接从事件上下文获取触发事件的节点,无须绑定 w:tree

上述例子的执行结果如下

运行结果

Figure 1. 运行结果


下面是一些常用的事件及对应的事件上下文

Table 1. 一些构件的事件及对应的事件上下文

事件 事件上下文 构件
onchange FieldChangeEvent w:textField, w:checkBox, w:calcNumberField, w:numberField, w:textArea, w:combo, w:timeField, w:dateField, w:dateTimeFielde
oncheck CheckEvent w:checkBox
onselect SelectionChangedEvent w:combo, w:timeField
onrowselect RowSelectEvent w:dataGrid, w:editDataGrid
onrowdeselect RowDeselectEvent w:dataGrid, w:editDataGrid
oncellselect CellSelectEvent w:dataGrid, w:editDataGrid
ondblclick RowDblClick w:dataGrid, w:editDataGrid
onclick, oncheck, oncollapsenode, onexpandnode, onselect TreeNodeEvent w:tree

现在我们知道,一些事件的处理方法有特定的事件上下文来获取信息,那么对于更多的事件呢?例如在一些情况下,我们需要获取触发事件的构件,这个需求能否满足呢?答案肯定的。2.3版本中定义了一个 AjaxActionEvent 的事件上下文类,这个类是上表中所有事件上下文的父类,所有的事件处理方法都可用这个类作为参数,如响应 w:textField 的 onblur 事件时,可以这么写

public void field_onblur(AjaxActionEvent event) {
    event.getTargetComponent();  // 获取事件源构件
}

AjaxActionEvent 类提供了三个常用方法,如下

  • public UIComponent getTargetComponent()

    获取触发事件的构件

  • public Map<String, Object> getParamsMap()

    获取传递的参数

  • public Object getParameter(String)

    获取传递的参数,等同于 getParamsMap().get(String)

关于传递参数在后面进行说明。

2.2. 自动匹配最佳方法

从上面我们可知,事件对应的方法签名可以有两种或三种,如对于以下页面

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE HTML PUBLIC "" "">
<f:view xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core"
	xmlns:w="http://www.apusic.com/jsf/widget" xmlns:layout="http://www.apusic.com/jsf/layout"
	xmlns:h="http://java.sun.com/jsf/html" xmlns:ajax="http://www.apusic.com/jsf/ajax"
	renderKitId="AJAX">
	<w:head>
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
	</w:head>
	<w:page title="Insert title here">
		<w:form>
			<w:textField>
				<ajax:action event="onchange" action="#{fieldBean.field_onchange }"></ajax:action>
			</w:textField>
		</w:form>
	</w:page>
</f:view>

LiteBean中响应方法的签名可以是以下三种签名的任意一种

	public void field_onchange() {
		System.out.println("field_onchange() executed!");
	}
	
	public void field_onchange(AjaxActionEvent event) {
		System.out.println("field_onchange(AjaxActionEvent event) executed!");
	}
	
	public void field_onchange(FieldChangeEvent event) {
		System.out.println("field_onchange(FieldChangeEvent event) executed!");
	}

但是,如果这三个方法同时出现了,这些方法会都执行吗?这就要涉及自动匹配最佳方法这个有趣的特性了。在上面例子中,当后台出现这几个方法时,签名为 field_onchange(FieldChangeEvent event) 的方法将会被调用,其他方法不会被调用。OperaMasks SDK中对事件方法的查找顺序如下:先查找参数为特定的事件上下文的方法(如field_onchange(FieldChangeEvent event)),如存在即执行;否则继续查找参数为 AjaxActionEvent 的方法(如field_onchange(AjaxActionEvent event) ),如存在即执行;否则继续查找无参的方法(如field_onchange()),如存在即执行,否则抛出找不到指定方法的异常。上面例子的执行结果如下

自动匹配最佳方法例子运行结果

Figure 2. 自动匹配最佳方法例子运行结果


 

2.3. 动态添加侦听器

除了使用EL方式和IoVC方式为构件添加侦听器外,2.3还支持动态添加侦听器,如下

@Bind
private UITextField txt;

public void txt_onchange(AjaxActionEvent e){
   // do something here
}

 @BeforeRender
public void beforeRender(boolean isPostback){
  if(!isPostback){
     txt.getAjaxEventHandler().addELBinding("onchange", "#{bean.txt_onchange}", false);1
  }
}
1

addElBinding() 方法的三个参数分别表示事件名称(如"onchange"),action方法的EL表达式(如"bean.txt_onchange")和immediate属性(true或false)。

其中, getAjaxEventHandler() 方法为 AjaxActionSupport 接口定义的方法。

2.4. 传递自定义参数

虽然OperaMasks SDK的事件机制提供了一些事件上下文,但是对于WEB来说,我们需要许多的参数,SDK提供的事件上下文不一定够用,我们可能需要传递一些自定义参数,如何解决呢?用隐藏 input 传递参数吗?可以,但实在太土了,我们可以用 2.3 提供的 ajax:param 构件传递自定义参数,如下

<w:form>
    <w:textField>
        <ajax:action event="onblur" action="#{bean.field_onblur}">
          <ajax:param name="company" value="apusic"></ajax:param>  
        </ajax:action>
    </w:textField>
</w:form>

这里可能有人会问,看起来这个 ajax:param 与 f:param 并无不同,为何要重造一个轮子呢?这个 ajax:param 其实还有一个特别之处,那就是它的值可以是 Javascript 变量(或者说,Javascript表达式)。如下,w:textField 构件在 onblur 事件发生时同时发送自身的值作为参数

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE HTML PUBLIC "" "">
<f:view xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core"
	xmlns:w="http://www.apusic.com/jsf/widget" xmlns:layout="http://www.apusic.com/jsf/layout"
	xmlns:h="http://java.sun.com/jsf/html" xmlns:ajax="http://www.apusic.com/jsf/ajax"
	renderKitId="AJAX">
	<w:head>
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
	</w:head>
	<w:page title="Insert title here">
		<w:form>
			<w:textField>
				<ajax:action event="onblur" action="#{txtBean.txt_onblur }">
					<ajax:param name="fieldValue" value="arguments[0].getValue()"1 isJsExpression="true"2>
					</ajax:param>
				</ajax:action>
			</w:textField>
		</w:form>
	</w:page>
</f:view>
1

通过查阅 ExtJS 的文档可知,ExtJS 在触发 blur 事件时传递了 TextField 自身作为第一个参数,所以此处 arguments[0] 为 TextField.

 

传递 JavaScript 变量需要设置 isJsExpression 属性为true(默认为false)

后台LiteBean对应方法为

	public void txt_onblur(AjaxActionEvent event) {
		System.out.println(event.getParameter("fieldValue"));
	}

运行上述例子,访问页面,输入"operamasks"并切换焦点

ajax:param例子

Figure 3. ajax:param例子


后台打印如下

ajax:param例子运行结果

Figure 4. ajax:param例子运行结果


当然,这儿的 ajax:param 传递的Javascript参数值最好是boolean, number, string等基本类型,不然,后台获取的值就是Javascript变量调用 toString() 方法后的值。

3. 更多功能

OperaMasks 2.3版本不仅提供了一系列的新特性,还支持了一些以往欠缺的事件,如Field构件的key事件支持和DataGrid的cellselect事件支持。用过 w:dataGrid 构件的同学可能有过这样的经历,在一个 selectionModel 属性设置为 CellSelectModel 的 w:dataGrid 中,如果先响应用户的 cellselect 事件,很难做到。在 2.3 中,这个不再是问题,如下

<w:dataGrid selectionModel="#{new org.operamasks.faces.component.grid.CellSelectionModel()}">
    <ajax:action event="oncellselect" action="#{bean.grid_oncellselect}">
    </ajax:action>
</w:dataGrid>
public void grid_oncellselect(CellSelectEvent event) {
   event.getRowIndex();
   event.getColIndex();1
}
1

从事件上下文中获取选中的格子在整个表格的第几行第几列

OperaMasks 2.3的事件机制介绍到此告一段落,更多内容,可参考OperaMasks在线文档

 

所有评论
mfkvfn 2009-12-24 评论道:
在发送请求之前,计算arguments[0].getValue()的值,然后为ajax:action发送的Ajax请求中添加此参数。然后在ajax:action组件的decode时以ajax:action和它所有的ajax:param子组件的值来构造一个AjaxActionEvent。然后广播事件。当事件监听时执行到public void txt_onblur(AjaxActionEvent event)方法的event.getParameter("fieldValue")时直接从AjaxActionEvent的params这个Map中取得value值就可以了。
风的选择 2009-11-02 评论道:
我想问一下 这里的value值是怎么传递的?
1   共1页
您还没有登录,请登录后发表评论