在JSP页面调用USER COMMAND

标签:SAP, Java

在开发ISR form时,经常会遇到前后台交互的问题。而我现在遇到的一个需求:当用户输入完一个InputField后,就立刻刷下后台来检查或修正一些字段。看上去很简单的玩意,但在ISR框架下却折腾了我许久。

最简单的触发后台USER COMMAND的方式就是创建一个按钮,这样点击按钮时就会以按钮的id作为USER COMMAND传递到后台了:
<hbj:button id='COST_CTR'
	jsObjectNeeded="true"
	onClick='submit'
	design='<%= ButtonDesign.STANDARD.toString() %>'
/>
这里的onClick必须是submit或onSubmit,原因后面阐述。

但是我们需要的是自动触发USER COMMAND,所以需要把这个按钮隐藏,并且在InputField输入完后触发点击这个按钮:
<hbj:inputField id="COST_CTR"
	type="String"
	value='<%= ISR.getValue("COST_CTR") %>'
	size='<%= ISR.getSize("COST_CTR") %>'
	maxlength="8"
	width="100"
	jsObjectNeeded="true"
>
	<%
		COST_CTR.setClientEvent(EventTrigger.ON_CHANGE, "raiseEvent('CHECK_COST_CTR', true);");
	%>
</hbj:inputField>

<span style="display:none">
	<hbj:button id='CHECK_COST_CTR'
		jsObjectNeeded="true"
		onClick='submit'
		design='<%= ButtonDesign.STANDARD.toString() %>'
	/>
</span>
注意COST_CTR只有在结束标签</hbj:inputField>关闭前有效。这个raiseEvent()函数就是根据按钮的id来查找按钮,然后触发它的click事件;由于不同框架涉及的代码不一样,因此源码就不列出了。

可惜这种方法只能粗略地满足需求,如果用户在输入完后直接点了个按钮,那么就可能失效了。
要弄清原因的话必须搞懂如下过程:
  1. 用户输入一个值,点击按钮A。
  2. 此时产生了2个事件:InputField的值被更改,按钮A被点击。
  3. 因为JavaScript是单线程的,所以这2个事件按照触发的时间顺序来执行。因此raiseEvent()将被调用,然后产生了点击隐藏按钮B的事件。
  4. 第一个事件执行完,开始处理按钮A被点击的事件。在处理这个事件时,它如果将htmlbevent.cancelSubmit设为true,或者调用另一个USER COMMAND,那么点击隐藏按钮B的事件就无法触发USER COMMAND了。

要解决这个问题,最容易想到的就是修改按钮A的逻辑,当发现InputField被修改过时,就忽略自身的功能。可是一个页面上有多个InputField和按钮,这样它们的代码就都混杂在一起,变得很难维护了。
另一个很重要的原因就是点击一个dropdown list时,我们的事件也会被无效,而且没有办法去捕捉和修改这个点击事件。

因此必须在捕捉到事件后直接调用USER COMMAND,这样后续的事件就无法阻止submit事件了。
于是我翻了下文档,找到了setServerEvent()方法:
<%@ page import="com.sapportals.htmlb.event.Event"%>
<%@ page import="com.sapportals.htmlb.event.ButtonClickEvent"%>
<hbj:inputField id="COST_CTR"
	type="String"
	value='<%= ISR.getValue("COST_CTR") %>'
	size='<%= ISR.getSize("COST_CTR") %>'
	maxlength="8"
	width="100"
	jsObjectNeeded="true"
>
	<%
		Event event = new ButtonClickEvent();
		event.setAction("submit");
		COST_CTR.setServerEvent(EventTrigger.ON_CHANGE, event);
	%>
</hbj:inputField>
这里为什么要用submit这个action呢?原因是我反汇编了IsrProcessEventPage这个dynpage,发现里面只提供了onCheck()和onSubmit()这2个可用于这种行为的handler:
public void onCheck(Event event)
  throws PageException
{
  createNewBean("CHECK");
}

public void onSubmit(Event event)
  throws PageException
{
  createNewBean(event.getComponentName());
}
其中onCheck只能触发CHECK事件,而onSubmit可以触发不同事件,因此就使用后者了。这也是按钮的onClick需要设置成submit的原因。

那么这个createNewBean()究竟做了什么呢?它实际上是创建了一个IsrBean对象,这个对象会去调用ISR_PROCESS_EVENT这个RFC,它的IsrEvent属性就是USER COMMAND的名字。
到此我终于知道ISR的运行机制了,原来也是靠RFC来调用…

遗憾的是这个event.getComponentName()返回的是HTMLB控件的id,我尝试过覆盖这个方法,结果失败了。
而一个页面上不能有id相同的HTMLB控件,所以当需要用按钮执行相同的后台逻辑时,就必须定义不同的id了。
好在后台ABAP在处理USER COMMAND时用的是CASE...WHEN语句,因此只要加一行WHEN语句就能搞定了。

最后顺带一提,反编译时我用的是JD-GUI。前几天在Google Reader上看到的玩意,没想到今天派上用场了…
虽然这东西很不错,但我希望能永远别再碰这东西了,苦逼的程序员你伤不起…

0条评论 你不来一发么↓

    想说点什么呢?