向导处理
目标
您已创建了一个 JSP 向导,可从用户那里收集一个或多个对象的相关信息。现在,您需要创建代码来处理该信息,并针对此 (这些) 对象执行数据库操作。
背景
如果您的向导使用某个内置按钮集,则当用户单击“完成”、“应用”、“保存”或“检入”按钮来提交表单时,将调用 javascript 函数来调用 ActionController 的 processRequest() 方法。WizardServlet 会将 HTTP 表单数据和其他向导上下文信息 (例如向导在何处启动) 加载到 NmCommandBean 中。然后将 NmCommandBean 传递到 FormDispatcher 类。FormDispatcher 将调用 FormProcessorController。
FormProcessorController 将表单数据分区到 ObjectBeans 中。为向导的每个目标对象创建一个 ObjectBean。包含特定于该对象的所有表单数据以及所有对象共用的任何表单数据。然后,FormProcessorController 将 ObjectBeans 传递给名为 ObjectFormProcessors 的类以执行该向导的相应任务,例如,在数据库中创建新对象、更新数据库中的对象或检入 ObjectFormProcessors 对象时,都可以调用名为 ObjectFormProcessorDelegates 的类以执行一个或多个子任务。
如果您的向导正在对单个对象执行某项操作,则可能需要创建自己的 ObjectFormProcessor 和/或 ObjectFormProcessorDelegates 以执行特定于您向导的任务。但是,如果您的向导正在创建或编辑某个对象,则可以利用该产品附带的一些处理器,以实现这些目的。
如果您的向导有多个目标对象,则您可能需要创建自己的 FormProcessorController 来控制对象的处理顺序,也可能不需要。
范围/适用性/假设
假定您已经创建了必要的 JSP、数据实用程序、GUI 组件和呈现器来显示您的向导。还假定您已经创建了将向导挂接到 UI 所需的操作。
预期结果
执行与一个或多个 Windchill 对象相关的数据库操作。
解决方案
使用 JSP 客户端体系结构框架和公用组件来处理向导表单数据,并对一个或多个对象执行相应的数据库任务。
必备知识
要实现此目标,需要了解以下内容:
• Java 编程
• 使用 HTML 表单的基本 Web 开发
• 熟悉执行向导的相应任务所需的 Windchill 服务 API 或其他 API
本节中使用的术语的定义:
术语
|
定义
|
目标对象
|
在向导中收集数据的对象。某些操作通常会在您的向导处理中执行这些对象。
|
解决方案元素
元素
|
类型
|
说明
|
ActionController
|
Java 类
|
向导表单数据在该类上发布,后者在处理完成后将响应页面发送回浏览器。
运行时位置:<Windchill>\srclib\CommonComponents-web.jar
|
FormProcessorController
|
Java 接口
|
实现此接口的类会实例化并调用 ObjectFormProcessor(s) 来执行向导任务。
运行时位置:<Windchill>\srclib\CommonComponents.jar
|
DefaultFormProcessorController
|
Java 类
|
FormProcessorController 的默认实现对于所有单对象向导都足够。此控制器将 HTML 表单数据分区到 ObjectBeans,并将这些 bean 传递至 ObjectFormProcessors。
具有多个目标对象的向导可能需要扩展此类,以控制对象的处理顺序。
运行时位置:<Windchill>\srclib\CommonComponents.jar
|
ObjectFormProcessor
|
Java 接口
|
实现此接口的类使用表单数据来执行与向导相应的数据库和相关任务。每个向导仅有一个 ObjectFormProcessor 类,但多个对象向导可能具有该类的多个实例。
ObjectFormProcessors 可能会调用 ObjectFormProcessor 委派来执行子任务。
运行时位置:<Windchill>\srclib\CommonComponents.jar
|
DefaultObjectFormProcessor
|
Java 类
|
ObjectFormProcessor 的默认实现,其中包含执行 ObjectFormProcessorDelegates 和多个其他常见任务的逻辑。此类为应由向导特定处理器扩展的基类。
运行时位置:<Windchill>\srclib\CommonComponents.jar
|
ObjectFormProcessrDelegate
|
Java 接口
|
实现此接口的类由 ObjectFormProcessors 调用以执行处理子任务。多个 ObjectFormProcessorDelegates 可由一个处理器调用,多个处理器可能会使用相同的委派来处理多个向导所共有的任务。这些是可选的。
运行时位置:<Windchill>\srclib\CommonComponents.jar
|
DefaultObjectFormProcessorDelegate
|
Java 类
|
ObjectFormProcessorDelegate 的默认实现。为可能不需要实现子类的方法提供了 no-op (无操作) 行为。此类为应由任务特定委派扩展的基类。
运行时位置:<Windchill>\srclib\CommonComponents.jar
|
ObjectBean
|
Java 类
|
特定目标对象的表单数据的容器,以及所有对象的公用数据。提供检索给定对象的表单数据的方法。
运行时位置:<Windchill>\srclib\CommonComponents.jar
|
ProcessorBean
|
Java 类
|
ObjectBeans 的容器,它知道应由相同的处理器实例处理哪个 ObjectBeans,以及处理顺序。
运行时位置:<Windchill>\srclib\CommonComponents.jar
|
FormResult
|
Java 类
|
用于将方法结果在服务器方法之间传递以及从服务器传递到 WizardServet 的类。
运行时位置:<Windchill>\srclib\CommonComponents.jar
|
下图所示的 UML 图表中显示了向导处理框架中主 Java 类之间的关系。
表单处理任务流
通常,在处理表单数据时,会对向导目标对象执行多个任务。这些通常遵循以下序列:
1. 预处理
在此阶段中,将完成数据库操作的设置。例如:
◦ 对于对象创建向导,将创建对象的实例,并通过表单数据中的值设置其属性。
◦ 对于对象编辑向导,将从数据库中检索对象,并根据表单数据中的值修改其属性。
2. 启动数据库事务处理块
3. 执行数据库操作。例如:
◦ 对于对象创建向导,对象将存储在数据库中
◦ 对于对象编辑向导,对象将在数据库中进行更新
4. 后处理
执行需要在主数据库操作后但在同一个事务处理块内完成的数据库或其他任务,以便在这些任务失败时回滚数据库操作。这些任务通常要求目标对象预先持续。例如:
◦ 将对象共享到另一个容器
◦ 将对象提交至工作流
5. 结束数据库事务处理块
6. 后事务处理
执行在事务处理块关闭后需要完成的任务,以便在任务失败时不会回滚整个数据库事务处理。例如:
◦ 检出对象
可根据需要在此阶段中执行其他数据库操作。
每个处理阶段中的任务均由 ObjectFormProcessors 和 ObjectFormProcessorDelegates 执行。每个向导必须有一个应扩展 DefaultObjectFormProcessor 的 ObjectFormProcessor。这是控制和执行向导相应的处理任务的主要类。
向导可能具有一个或多个由 ObjectFormProcessor (通过 DefaultObjectFormProcessor) 调用来执行处理子任务的 ObjectFormProcessorDelegates,也可能不具有。ObjectFormProcessorDelegates 应扩展 DefaultObjectFormProcessorDelegate 类。有关 ObjectFormProcessorDelegates 的详细信息,请参阅为您的向导创建任何必需的 ObjectFormProcessorDelegate 类。
ObjectFormProcessor 和 ObjectFormProcessorDelegate 类都具有用于在每个处理阶段执行任务的 preProcess()、doOperation()、postProcess() 和 postTransactionProcess() 方法。
| 用户导航向导步骤时,将调用向导步骤验证器。要准备必须对其执行验证的对象,将执行 ObjectFormProcessor 和 ObjectFormProcessorDelegate 类的 preprocess() 方法。 |
并非每个向导都在每个阶段有任务,因此,如果每个处理器和每个委派均分别从默认类 DefaultObjectFormProcessor 和 DefaultObjectFormProcessorDelegate 中进行扩展,则无需执行所有这些方法。这些父类提供每种方法的默认实现。如果 ObjectFormProcessor 实现了这些方法中的一种方法,则应调用 DefaultObjectFormProcessor 的 super() 方法来处理 ObjectFormProcessorDelegates 的调用。
HTML 表单数据将在 ObjectBeans 列表中传递到 ObjectFormProcessors 和 ObjectFormProcessorDelegates。ObjectBean 包含特定于一个目标对象的数据以及所有目标对象的公用数据。对于具有单个目标对象的向导,该列表仅包含一个 ObjectBean。对于具有多个目标对象的向导,该列表对每个目标对象包含一个 ObjectBean,并且 ObjectBeans 可能会被组织到代表对象之间的关系及其处理顺序的树结构中。ObjectBeans 的创建由 FormProcessorController 进行处理。
FormProcessorController 还处理数据库事务处理的开始、结束和回滚 (如有必要),并对 ObjectFormProcessors 进行调用。对于后者,DefaultFormProcessorController 使用名为 ProcessorBeans 的对象。属于相同类型、具有相同 ObjectFormProcessorDelegates 且具有相同父对象的目标对象将被放置到相同的 ProcessorBean 中。
每个 ProcessorBean 对于其包含的 ObjectBeans 也都有各自的 ObjectFormProcessor 和 ObjectFormProcessorDelegates 实例。ProcessorBeans 可能被组织到树结构中,以控制对象的处理顺序。(具有单个目标对象的向导将仅有一个 ProcessorBean。)
DefaultFormProcessorController 的任务流如下所示:
1. 调用 ProcessorBean 根的 ObjectFormProcessor 的 preProcess() 方法,并在 ProcessorBean 中将其传递到 ObjectBeans。然后按照在子列表中出现的顺序,调用 ProcessorBean 根的子项处理器的 preProcess() 方法。然后调用子项的子项的 preProcess() 方法,依此类推。
2. 执行 Transaction.start()。
3. 以与 preProcess() 相同的方式调用 ProcessorBean 根及其子项的 ObjectFormProcessor 的 doOperation() 方法。
4. 以与 preProcess() 相同的方式调用 ProcessorBean 根及其子项的 ObjectFormProcessor 的 postProcess() 方法。
5. 如果步骤 1-4 成功,则执行 Transaction.commit()。
6. 以与 preProcess() 相同的方式调用 ProcessorBean 根及其子项的 ObjectFormProcessor 的 postTransactionProcess() 方法。
如果任何方法返回的状况为 FormProcessingStatus.FAILURE,则控制器将调用失败的 ObjectFormProcessor 的 setResultNextAction() 方法,以便可以设置 HTML 响应页面所需的信息。
| ObjectFormProcessors 不应在步骤 3 或 4 中打开/提交其他事务处理块,因为不建议使用事务处理的嵌套。 |
具有多个目标对象的向导
支持两种类型的多对象向导:
• 具有多个不相关目标对象的向导
示例:创建多个部件
• 具有相关目标对象的树的向导
示例:创建更改通告和相关更改任务
特定于给定对象的数据应包含在特定于该对象的表格行或特定于该对象的向导步骤中。示例表格中的每行表示一个正在创建的部件,每列都是该部件的一个属性,如下所示。
每个向导步骤必须显示以下数据类型之一:
• 以表格格式表示的数据,其中每行代表不同的对象
• 特定于一个且仅其中一个已创建对象的数据
• 所有已创建对象共用的数据
步骤不能包含多个对象的对象特定数据,除非其为表格格式。
在多对象向导中,输入字段应用的对象由在 HTML 输入字段的名称属性中嵌入的 "objectHandle" 来标识。例如:
<input id="null1188140328133"
name="<someFieldIdString>!~objectHandle~newRowObj_430512131997223~!
<someAdditionalText>" value="" size="60" maxlength="60"
type="text">
在上面的示例中,"newRowObj_430512131997223" 是对象句柄,"!~objectHandle~" 是必需的前缀,而 "~!" 是必需的后缀。嵌入对象句柄的 HTML 名称属性可以是任何字符串,并且对象句柄可能会出现在字符串内的任意位置。
当 DefaultFormProcessorController 将表单数据加载到 ObjectBeans 中时,它将从名称属性中去除对象句柄 (包括所需的前缀和后缀),并在表单数据参数映射中使用生成的字符串作为值的密钥。例如,要检索上方输入字段的表单值,请使用以下键调用 ObjectBean.getTextParameter():
<someFieldIdString><someAdditionalText>
框架通过以下两种方式之一为您生成名称属性的对象句柄:
• 如果以表格格式捕获对象的数据,其中每行代表一个对象,每列代表一个对象的属性,则如果在构建器中的表格配置上设置 rowBasedObjectHandle=true,则会为您动态生成该句柄:
table.setRowBasedObjectHandle(true);
每行的对象句柄将基于行的 OID。
objectHandle = CreateAndEditWizBean.getNewObjectHandle(next.getOid().toString());
• 如果给定向导步骤中的所有数据都针对同一对象,则可在向导步骤操作中指定该对象的对象句柄:
<jca:wizardStep action="setContextWizStep" type="object"
objectHandle="<your object handle string>" …
如果向导步骤中的数据对于所有已创建的对象都是公共的,则输入字段不需要任何对象句柄。与 ObjectBean 中的数据关联的对象句柄可通过 ObjectBean.getObjectHandle() 方法进行访问。FormProcessorController 控制处理器的调用顺序以处理 ObjectBeans,如以下各节所述。请注意,在下图中,圆用于表示 ObjectBeans。表示相同类型的对象的圆将具有相同的着色。
多个不相关的目标对象
通常,当向导包含多个不相关的目标对象时,这些对象的类型是相同的:
此类向导的一个示例是,您可以在其中创建多个部件。此向导包含三个步骤:
1. 定义部件
用户输入要创建的零件类型以及所有正在创建的部件共用的其他属性。
2. 设置属性
用户输入要以表格格式创建的每个部件的名称和编号。此表格是动态的,用户可输入任意数量的部件。
3. 设置附加属性
用户输入所有部件共用的一些其他属性。
如果用户为五个部件输入数据,DefaultObjectFormProcessorController 将创建五个 ObjectBeans。每个 ObjectBean 将包含步骤 2 中特定于其所表示部件的数据以及步骤 1 和步骤 3 中的数据。由于部件之间没有关系,且可以独立创建,因此 ObjectBeans 没有父项或子项。由于相同的 ObjectFormProcessor 和 ObjectFormProcessorDelegates 将用于处理所有 ObjectBeans,并且所有对象均属于同一类型,因此它们将全部放置于相同的 ProcessorBean 中。
多个相关的目标对象
其他向导可能有多个相关的目标对象。例如,您可能具有创建更改通告的向导以及与该更改通告相关的更改任务。要在更改通告和更改任务之间创建关联,更改通告的处理器将需要了解对象之间的相互关系。
ObjectBean 更改通告有三个 ObjectBeans 子项。ObjectBeans 更改任务有 ObjectBean 父项,没有子项。
在这种情况下,您需要编写自己的 FormProcessorController 来创建 ObjectBeans 的结构。可以是 DefaultFormProcessorController 的子类。
默认控制器将为您创建 ObjectBeans。您将覆盖其 createObjectBeanStructure() 方法,为其分配所有 ObjectBeans 的展平式列表。在该方法中,您将设置 ObjectBeans 的父项和子项。您传递回所有 ObjectBeans 根的列表。创建 ObjectBean 结构后,DefaultFormProcessorController 将调用 ProcessorBean.newCollection() 方法,并按如下方式将 ObjectBeans 分组到 ProcessorBeans 中:
在上图中,圆表示 ObjectBeans,实线表示它们之间的关系。矩形表示两个 ProcessorBeans,虚线表示它们之间的关系。
每个 ProcessorBean 都有自己的 ObjectFormProcessor 实例,ObjectFormProcessorDelegates 为其中的对象所需。如果 ProcessorBean 根的处理器名为 "ProcessorInstance1",且该 ProcessorBean 子项的处理器名为 "ProcessorInstance2",则将按如下方式调用处理器方法:
| 方法 | 任务已执行 |
1 | ProcessorInstance1.preProcess(处理器 Bean 1 中的 ObjectBean) | 创建 WTChangeOrder2 的实例并将其存储在 bean 的 "object" 属性中 |
2 | ProcessorInstance2.preProcess(处理器 Bean 2 中的 ObjectBeans) | 创建 WTChangeActivity2 的三个实例并将其存储在 bean 的 "object" 属性中 |
3 | ProcessorInstance1.doOperation(处理器 Bean 1 中的 ObjectBean) | 保持 WTChangeOrder2 |
4 | ProcessorInstance2.doOperation(处理器 Bean 2 中的 ObjectBeans) | 保持 WTChangeActivity2 实例 |
5 | ProcessorInstance1.postProcess(处理器 Bean 1 中的 ObjectBean) | 创建更改通告和更改任务之间的关联 |
6 | ProcessorInstance2.postProcess(处理器 Bean 2 中的 ObjectBeans) | 无 |
7 | ProcessorInstance1.postTransactionProcess(处理器 Bean 1 中的 ObjectBean) | 无 |
8 | ProcessorInstance2.postTransactionProcess(处理器 Bean 2 中的 ObjectBeans) | 无 |
任务的排列方式可以不同。例如,您可以在方法 6 而不是方法 5 中创建效果相同的关联。或者,您可以创建 ObjectFormProcessorDelegate 来在 postProcess() 方法中创建关联。通过框架可以灵活性地模块化您的代码,使其最适合您的向导。只需确保相对于主事务处理的开始和结束正确排列任务。
您的 ObjectBeans 结构可能会更加复杂。例如:
正如您在上图中所看到的那样,如果下列任一项为 true,ObjectBeans 将放置于不同的 ProcessorBeans:
• ObjectBeans 中的对象具有不同的类型
• ObjectBeans 具有不同的 ObjectFormProcessor
(注意:此时,向导中的所有 ObjectBeans 必须具有相同的 ObjectFormProcessor。)
• ObjectBeans 具有不同的 ObjectFormProcessorDelegates 列表
• ObjectBeans 具有不同的 ObjectBean 父项
DefaultFormProcessorController 将调用与每个 ProcessorBean (从 ProcessorBean 根处开始并将树向下移动到 ProcessorBeans 叶) 相连接的处理器。
过程 - 创建向导处理代码
创建向导处理代码时有两个选项:
• 具有单个目标对象的向导
• 具有多个目标对象的向导
具有单个目标对象的向导
此进程包括以下步骤:
• 创建处理器类
• 在向导操作上为向导指定处理器类
• 为您的向导创建任何必需的 ObjectFormProcessorDelegate 类
• 指定要在向导中使用的 ObjectFormProcessorDelegate(s)
创建处理器类
产品附带的用于处理对象创建和编辑向导的三个处理器:
• com.ptc.core.components.forms.CreateObjectFormProcessor
• com.ptc.core.components.forms.DefaultEditFormProcessor
• com.ptc.core.components.forms.EditWorkableFormProcessor
根据各自的用途,这些处理器可按现在的方式进行使用或扩展。
如果您的向导不是对象创建或编辑向导,则需要创建自己的 ObjectFormProcessor。ObjectFormProcessors 应扩展 DefaultObjectFormProcessor 类。
应根据需要将表单处理逻辑放入处理器的 preProcess()、doOperation()、postProcess() 和 postTransactionProcess() 方法中。您的方法应在 DefaultObjectFormProcessor 中调用相应的 super 方法来处理对 ObjectFormProcessorDelegates 的调用。这些方法将传递给单个 ObjectBean,包含向导中的所有表单数据。可使用该对象的 getter 方法访问该表单数据。通常使用以下 getter 方法:
public Map<String,List<String>> getChangedComboBox()
public Map<String,String> getChangedRadio()
public Map<String,String> getChangedText()
public Map<String,String> getChangedTextArea()
public Map<String,List<String>> getChecked()
public Map<String,List<String>> getUnChecked()
public List getRemovedItemsByName(String paramName)
public List getAddedItemsByName(String paramName)
public String getTextParameter(String key)
public String[] getTextParameterValues String key)
有关详细信息,请参阅 Javadoc。
ObjectBean 的 "object" 属性表示目标对象的实例。在适当情况下,应根据下游方法使用其中一种处理器方法 (很可能是 preProcess()) 进行设置。其他信息可以使用处理器实例变量从一种方法传递到另一种方法。
传递到这些方法的 NmCommandBean 对象包含启动向导时所处页面的信息,以及该向导启动时父页面上选定的对象。其中还包含所有 HTML 表单数据,但应在 ObjectBean 中而不是在 NmCommandBean 中使用方法来访问该数据。有关详细信息,请参阅 NmCommandBean 的 javadoc。
您可以使用 com.ptc.core.component.FormResult 对象将 preProcess()、doOperation()、postProcess() 和 postTransactionProcess() 方法的结果传递回 DefaultFormProcessorController。在返回之前,这些方法中的每一种都应调用 FormResult.setStatus() 以返回处理状况。有三个选项可供使用:
• FormProcessingStatus.SUCCESS - 如果方法执行时未出错
• FormProcessingStatus.FAILURE - 如果方法遇到致命错误 FormProcessingStatus.NON_FATAL_ERROR - 如果方法成功,但遇到一个或多个应向用户报告的问题
DefaultFormProcessorController 将返回的 FormResult 传递给其 continueExecuting() 方法,以确定处理应继续到下一阶段还是中止。默认情况下,只有状态为 FormProcessingStatus.FAILURE 时才会中止。如果要在处理过程中中止或完成所有处理,则控制器将在 ObjectFormProcessor 中调用 setResultNextAction() 方法以在 FormResult 中设置所需的信息,以构造发送回浏览器的响应页面。
此方法应传达以下信息:
• 反馈消息应显示给用户 (如果有)。
由 feedbackMessages 和异常变量确定。
在执行任何窗口操作或提供的 javascript 之前,会显示反馈消息 (如果有)。
只有状况为 FormProcessingStatus.FAILURE 或 FormProcessingStatus.NON_FATAL_ERROR 时才会显示异常消息。
有关详细信息,请参阅 FormResult 和 FormProcessingStatus 类的 javadoc。
也可以使您的 ObjectFormProcessor、preProcess()、doOperation()、postProcess() 和 postTransactionProcess() 方法抛出异常。在这种情况下,控制将返回到 ActionController,按如下所示设置 FormResult 的变量:
• status - FormProcessingStatus.FAILURE
• exceptions - 抛出的异常
这将导致响应页面在警报窗口中显示异常消息,然后关闭向导窗口。
如何使用 NmCommandBean 在表单处理器中获取选定对象
有四种不同的表单处理方案,要在每个方案中获取选定的 OID,可以使用 NmCommandBean 中的不同 API。有关详细信息,请参阅下图。
在向导操作上为向导指定处理器类
在向导的 <action> 标记的 <command> 子标记中指定 ObjectFormProcessor 类。您的操作标记将包含在一个 *actions.xml 文件中。
以下是如何将 CreateDocFormProcessor 类指定为您的处理器的示例。
<action name="create">
<command
class="com.ptc.core.components.forms.CreateObjectFormProcessor"
windowType="popup" />
</action>
为您的向导创建任何必需的 ObjectFormProcessorDelegate 类
ObjectFormProcessorDelegates 可用于在您的向导处理中执行一个或多个子任务。由于相同的委派类可能由多个 ObjectFormProcessors 调用,因此它们通常用于多个向导所需的任务。以下是如何在已交付的产品中使用 ObjectFormProcessorDelegates 的一些示例:
• 多个对象创建向导具有名为“检入后保持检出状态”的复选框。所有这些向导的 ObjectFormProcessors 都调用相同的 ObjectFormProcessorDelegate 类来处理此复选框,并在创建该对象后对其进行检出 (如果选中此框)。
• 用于创建对象的向导 ContentHolders 通常有一个“设置附件”步骤,用于指定应附加到对象的特定文档。所有这些向导都调用相同的 ObjectFormProcessorDelegate 类来处理附件的持续性。
• 许多对象创建向导具有 Location 属性的输入字段,用于指定对象的文件夹。由于此输入字段的处理很复杂,因此所有这些向导都在正在创建的对象上调用相同的 ObjectFormProcessorDelegate 来设置文件夹。
如上面的示例所示,一个 ObjectFormProcessorDelegate 可以处理一个或多个输入字段的处理。如果您的向导不具有在多个向导中使用的任何 HTML 元素,则可能不需要任何委派。但是,它们也有助于模块化您的处理代码。
ObjectFormProcessorDelegates 应扩展 DefaultObjectFormProcessorDelegate 类。ObjectFormProcessorDelegates 由 DefaultFormProcessorController 进行实例化并传递到 ObjectFormProcessor。
与 ObjectFormProcessors 一样,ObjectFormProcessorDelegates 具有 preProcess()、doOperation()、postProcess() 和 postTransactionProcess() 方法。为向导注册的委派将由 DefaultObjectFormProcessor 在不同的处理阶段调用。
使用 FormResult 将 ObjectFormProcessorDelegate 方法的结果传递回 ObjectFormProcessor,就如同这些 ObjectFormProcessor 方法将其结果传递回 FormProcessorController 一样。
与 ObjectFormProcessor 方法一样,ObjectFormProcessorDelegate 方法可能会抛出异常。如何处理这些方法取决于 ObjectFormProcessor 对其的调用。
指定要在向导中使用的 ObjectFormProcessorDelegate(s)
由 DefaultObjectFormProcessor 实例化的 ObjectFormProcessorDelegate 类的名称 (如果有) 将在隐藏的输入字段中进行通信,如下所示:
<input name="FormProcessorDelegate"
value="com.ptc.core.components.forms.NumberPropertyProcessor"
type="hidden">
可通过以下几种方式创建这些隐藏的输入字段:
• 如果您具有与特定向导步骤关联的委派,则可在向导步骤操作的命令子标记中指定该委派。例如:
<action name="attachmentsWizStep" postloadJS="preAttachmentsStep"
preloadWizardPage="false"
<command
class="com.ptc.windchill.enterprise.attachments.forms.Second
aryAttachmentsSubFormProcessor" windowType="wizard_step"/>
</action>
然后,向导框架将在向导中为使用此步骤的任何向导生成一个隐藏的 FormProcessorDelegate 字段。如果已在向导步骤操作中指定了对象句柄,则隐藏字段的名称属性将包括对象句柄,以便仅为与该步骤关联的目标对象调用委派。
• 如果您具有需要委派的特定输入字段,则可以在数据实用程序中生成隐藏字段,以创建输入字段的 GUI 组件。建议数据实用程序返回一个 AbstractGuiComponent 子类,以利用 addHiddenField() 方法和 AbstractRenderer。在数据实用程序中创建 GUI 组件后,在 AbstractGuiComponent 类中调用 addHiddenField() 方法。例如:
LocationInputGuiComponent guiComponent = new
LocationInputComponent(…);
guiComponent.addHiddenField
(CreateAndEditWizBean.FORM_PROCESSOR_DELEGATE, "com.
ptc.windchill.enterprise.folder.LocationPropertyProcess
or");
将由 AbstractRenderer 自动为您生成隐藏的输入字段。如果该字段与具有对象句柄的步骤或表格行关联,则该对象句柄将被嵌入到隐藏字段的 HTML 名称属性中。如果选取返回一个未扩展 AbstractGuiComponent 的 GUI 组件,则 GUI 组件和呈现器将必须知道如何呈现所需的隐藏字段。
• 您可以直接在 JSP 文件中包括您的委派的隐藏字段。但是,首选前两种方法中的一种,因为其中封装了隐藏字段及其关联的 HTML 输入字段。
具有多个目标对象的向导
此进程包括以下步骤:
• 创建处理器类
• 在向导操作上为向导指定处理器类
• 为您的向导创建任何必需的 ObjectFormProcessorDelegate 类
• 指定要在向导中使用的 ObjectFormProcessorDelegate(s)
• 根据需要创建 FormProcessorController
• 根据需要指定要在向导中使用的 FormProcessorController
创建处理器类
与具有单个目标对象的向导相同。请参阅创建处理器类。
在向导操作上为向导指定处理器类
与具有单个目标对象的向导相同。请参阅在向导操作上为向导指定处理器类。
为您的向导创建任何必需的 ObjectFormProcessorDelegate 类
与具有单个目标对象的向导相同。请参阅为您的向导创建任何必需的 ObjectFormProcessorDelegate 类。
指定要在向导中使用的 ObjectFormProcessorDelegate(s)
与具有单个目标对象的向导相同。请参阅指定要在向导中使用的 ObjectFormProcessorDelegate(s)。请记住,如果希望委派仅用于给定对象,则在指定该委派的隐藏字段的名称属性中必须嵌入对象句柄。
根据需要创建 FormProcessorController
如果您向导的目标对象构成相关对象的结构,则需要创建自己的 FormProcessorController 来创建 ObjectBeans 的结构。您应将 DefaultFormProcessorController 划分子类以利用其功能将表单数据分区到 ObjectBeans 和 ProcessorBeans,并调用表单处理器。通常,您仅需覆盖 createObjectBeanStructure() 方法。
根据需要指定要在向导中使用的 FormProcessorController
如果已创建自己的 FormProcessorController,则应在向导的主 JSP 文件中为向导标记指定要用于向导的控制器。例如:
<jca:wizard helpSelectorKey="change_createProblemReport"
buttonList="DefaultWizardButtonsWithSubmitPrompt"
formProcessorController="com.ptc.windchill.enterprise.change2.forms.
controllers.ChangeItemFormProcessorController">
FormResult/客户端后表单处理
从 Windchill 10.1 MR010 版本开始,表单处理和操作响应处理是分开的。这意味着表单处理器仍会执行更新/添加/删除选定/受影响对象的工作,但无需在 FormResult 上指定下一个操作。FormResult 将包含已更新/已添加/已删除对象的 OID。这些 OID 将被传递回客户端,客户端将更新当前显示这些对象的组件。
设置 FormResult
从 Windchill 10.1 MR010 版本开始,无需设置下一个操作 (即 setNextAction(FormResultAction.REFRESH_OPENER))。只有已添加/已更新/已移除对象的 OID 应添加到 FormResult 对象中。
示例:formResult.addDynamicRefreshInfo new DynamicRefreshInfo(newOid, oldOid, NmCommandBean.DYNAMIC_UPD))
在 FormResult 上设置 URL 或 Javascript 函数的功能仍有效。但是,只有在没有其他选项可用时才应执行此操作。由于多个客户端可能会调用这些操作,因此返回的 URL javascript 可能不适用,或者可能不会引用有效代码。理想情况下,您希望组件自行更新,而不是通过单独操作来更新页面上的组件。如果在 FormResult 上设置了 URL,则不会在启动操作的组件上调用 afteraction 监听程序。但是,将调用 objectsaffected 监听程序。
刷新/更新 JCA 组件
有两个监听程序可附加到通过操作更新的对象的组件中。大多数组件 (即表格、树、信息页面等) 都具有这两个事件的默认监听程序。
• afteraction:此监听程序负责更新在组件上启动/执行操作的对象。例如,如果在信息页面上执行检出操作,则信息页面 afteraction 监听程序负责刷新自身。同样,如果从表格中启动检出操作,则 afteraction 表格监听程序将负责更新表格中的行。通过添加 afteraction 事件监听程序,可将此监听程序连接到任何扩展组件。监听程序会将 FormResult JS 对象接收为 parameter.component.on(‘afteraction’, someAfterActionListener);
• objectsaffected:当从不同的组件执行操作时,此监听程序负责在组件中更新对象。例如,如果从信息页面执行检出操作,并且搜索结果表格中包含同一对象,则 [搜索结果] 表格中的 objectsaffected 监听程序将负责更新表格中的行。此监听程序将被附加到全局 PTC.action 对象:component.mon(PTC.action, ‘objectsaffected’,someListener);
自定义操作处理示例
在内部发生任何操作时刷新整个组件
me.on('afteraction', me.onAfterAction);
me.onAfterAction = function(formResult) {
me.refresh();
return true;//Stop further processing
};
FormResultAction.FORWARD 和 LOAD_OPENER_URL 的替换
不同的组件具有不同的行为,不同的客户端平台具有不同的 URL 模式,因此不支持常规尝试和在 FormResult 上指定 URL。每个组件都可以决定如何单独执行此操作,以及公用组件上的 afterAction 监听程序,如 infopage 和 miniNavigator 已具有刷新自身的通用逻辑。例如,miniNavigator 不希望转发到新的 URL,因为这样会超出当前上下文而中断用户,因此它会刷新自身。
有关如何使特定于组件的事件监听程序执行唯一的操作,请参阅下面的检出示例。请参阅 js 文件中的 PTC.miniNavigator.onAfterAction 和 PTC.infoPage.onAfterAction 代码。如果需要在客户端通过向下传递额外的 URL 参数来构造 URL,则可以使用该 FormResult.extraData 映射向下传递额外的信息。
您可以更改对象对特定表格的处理方式。例如,在检出表格中添加行,而不是在现有行中 (从 wip.jsfrag) 添加检出标志:
PTC.wip = {};
/**
* add/remove rows from the checkouts table for the wip actions
*/
PTC.wip.checkoutsTableObjectsAffectedWrapper = function(original, formResult) {
if (formResult.actionName === 'checkout') {
var added_new_rows = formResult.getUpdatedOids();
if(added_new_rows.length > 0) {
clearActionFormData();
rowHandler.addRows(added_new_rows, this.id, null, {
doAjaxUpdate : true,
addSorted : true
});
}
return true;
}
return original.call(this, formResult);
};
PTC.wip.addCheckoutTableListeners = function (table) {
table.onObjectsAffected = table.onObjectsAffected.wrap
(PTC.wip.checkoutsTableObjectsAffectedWrapper);
};
Ext.ComponentMgr.onAvailable('checkedout.work.table',
PTC.wip.addCheckoutTableListeners);
FormResultAction.JAVASCRIPT 的替换
如果需要在页面中运行额外的 javascript 以更新某一操作的特定组件,则最好添加组件特定的 objectsaffected 监听程序,而不是对特定操作的响应,如下面的检出操作所示。某些向导可能想要添加特定于该向导的监听程序,因为 javascript 在其他页面中通常不需要。
向导特定处理程序
向导中需要执行自定义或特定代码的操作可使用回调函数来处理成功的提交。此 successFunc 配置参数将传递给 PTC.wizard.submitWizard 函数。然后,当向导从服务器接收 FormResult 并开始触发 afteraction 和 objectsaffected 事件时,将调用此 successFunc。这样,操作所有者便可控制该向导的流动和行为,这比需要修改单个组件处理事件名称的方式更具体。例如,在将属性成功保存到数据库后,编辑向导中的 checkin 按钮包含一个用于启动 checkin 向导的成功的处理程序。
在下面的示例代码中,请注意函数传递给了向导代码:
<command onClick="onEditSubmit('checkinButton')"/>
function onEditSubmit(actionName){
var params = {successFunc: PTC.wizard.launchCheckinWizard,
finished: actionName=='saveButton'};
PTC.wizard.submitWizard(params);
}
更改对象对特定表格的处理方式
例如,可以在检出表格中添加行,而不是在现有行中 (从 wip.jsfrag) 添加检出标志:
PTC.wip = {};
/**
* add/remove rows from the checkouts table for the wip actions
*/
PTC.wip.checkoutsTableObjectsAffectedWrapper =
function(original, formResult) {
if (formResult.actionName === 'checkout') {
var added_new_rows = formResult.getUpdatedOids();
if(added_new_rows.length > 0) {
clearActionFormData();
rowHandler.addRows(added_new_rows, this.id, null, {
doAjaxUpdate : true,
addSorted : true
});
}
return true;
}
return original.call(this, formResult);
};
PTC.wip.addCheckoutTableListeners = function (table) {
table.onObjectsAffected = table.onObjectsAffected.wrap
(PTC.wip.checkoutsTableObjectsAffectedWrapper);
};
PTC.onAvailable('checkedout.work.table',
PTC.wip.addCheckoutTableListeners);
FormResultAction.FORWARD 和 LOAD_OPENER_URL 的替换
不同的组件具有不同的行为,不同的客户端平台具有不同的 URL 模式,因此不支持常规尝试和在 FormResult 上指定一个 URL。每个组件都可以决定如何单独执行此操作,以及公用组件上的 afterAction 监听程序,如 infopage 和 miniNavigator 已具有刷新自身的通用逻辑。 例如,miniNavigator 不希望转发到新的 URL,因为这样会超出当前上下文而中断用户,因此它会刷新自身。
有关如何使特定于组件的事件监听程序执行唯一的操作,请参阅下面的检出示例。另请参阅 js 文件中的 PTC.miniNavigator.onAfterAction 和 PTC.infoPage.onAfterAction 代码。如果需要在客户端通过向下传递额外的 URL 参数来构造 URL,则可以使用该 FormResult.extraData 映射向下传递额外的信息。 在大多数情况下,无需自动转发到刚刚创建或修改的对象的信息页面。创建操作后,会出现一条内联消息,并包含一个链接,允许其他人转至新对象信息页面。这是操作后可能自动转发的方式。修改组件的 onAfterAction 方法以包括此代码序列:
if (formResult.actionName === 'checkout') {
PTC.infoPage.goTo(formResult.getUpdatedOids()[0]);
}
return true;
可在所有可能为特定操作自动转发的组件中添加通用操作处理程序。以下代码示例对此进行了详细说明。但是,并不一定建议这样做,因为某些页面和组件可能会丢失正在进行的数据。
PTC.action.on('objectsaffected', function (formResult) {
if (formResult.actionName === 'checkout') {
PTC.infoPage.goTo(formResult.getUpdatedOids()[0]);
}
return true;
});
限制
• 当前仅支持每个向导包含一个 ObjectFormProcessor 类
• 框架不支持在同一向导步骤中包含多个对象的数据,除非为表格格式。也不支持在同一步骤中将所有对象的公用数据作为对象特定的数据。
更多资源
相关的包/类 Javadoc
• com.ptc.core.components.forms.FormDispatcher
• com.ptc.core.components.forms.FormProcessorController
• com.ptc.core.components.forms.DefaultFormProcessorController
• com.ptc.core.components.forms.ObjectFormProcessor
• com.ptc.core.components.forms.DefaultObjectFormProcessor
• com.ptc.core.components.forms.CreateObjectFormProcessor
• com.ptc.core.components.forms.DefaultEditFormProcessor
• com.ptc.core.components.forms.EditWorkableFormProcessor
• com.ptc.core.components.forms.ObjectFormProcessorDelegate
• com.ptc.core.components.forms.DefaultObjectFormProcessorDelegate
• com.ptc.core.components.forms.FormResult
• com.ptc.core.components.forms.DynamicRefreshInfo
• com.ptc.core.components.forms.FormProcessingStatus
• com.ptc.core.components.util.FeedbackMessage
• com.ptc.core.ui.resources.FeedbackType