Wizard Processing
Objective
You have created a JSP wizard to gather information from a user about one or more object(s). You now need to create the code to process that information and perform a database operation(s) on the object(s).
Background
If your wizard uses one of the built-in button sets, when a user clicks the Finish, Apply, Save, or Check In button to submit the form, a javascript function is called that invokes the processRequest() method of the ActionController. The WizardServlet loads the HTTP form data and other wizard context information, such as where the wizard was launched, into a NmCommandBean. It then passes the NmCommandBean to the FormDispatcher class. The FormDispatcher calls the FormProcessorController.
The FormProcessorController partitions the form data into ObjectBeans. One ObjectBean is created for each target object of the wizard. It contains all the form data specific to that object and any form data that is common to all objects. The FormProcessorController then passes the ObjectBeans to classes called ObjectFormProcessors that perform the tasks appropriate to the wizard --- for example, creating a new object in the database, updating an object in the database, or checking in an object ObjectFormProcessors, in turn, can call classes called ObjectFormProcessorDelegates to perform one or more subtasks.
If your wizard is performing an operation on a single object you may need to create your own ObjectFormProcessor and/or ObjectFormProcessorDelegates to perform the tasks specific to your wizard. However, if your wizard is creating or editing an object, you may be able to take advantage of some processors that are delivered with the product for those purposes.
If your wizard has multiple target objects you may or may not also need to create your own FormProcessorController to control the order in which objects are processed.
Scope/Applicability/Assumptions
Assumes that you have already created the necessary JSPs, data utilities, GUI components, and renderers to display your wizard. Also assumes that you have created the necessary actions to hook up your wizard to the UI.
Intended Outcome
Perform a database operation(s) related to one or more Windchill object.
Solution
Use the JSP client architecture framework and common components to process wizard form data and perform the appropriate database tasks for one or more object(s).
Prerequisite knowledge
To achieve this objective, you need to have an understanding of the following:
• Java programming
• Basic web development using HTML forms
• Familiarity with the Windchill service APIs or other APIs necessary to perform the tasks appropriate to the wizard
Definition of terms used in this section:
Term
|
Definition
|
target object
|
Object(s) for which you are gathering data in your wizard. Some operation(s) will typically be performed on these objects in your wizard processing.
|
Solution Elements
Element
|
Type
|
Description
|
ActionController
|
Java class
|
This is the class to which wizard form data gets posted and which sends the response page sent back to the browser after processing completes.
Runtime location: <Windchill>/srclib/CommonComponents-web.jar
|
FormProcessorController
|
Java interface
|
Classes implementing this interface instantiate and call ObjectFormProcessor(s) to execute wizard tasks.
Runtime location: <Windchill>/srclib/CommonComponents.jar
|
DefaultFormProcessorController
|
Java class
|
A default implementation of FormProcessorController that should be sufficient for all single-object wizards. This controller partitions the HTML form data into ObjectBeans and passes those beans to ObjectFormProcessors.
Wizards with multiple target objects may need to extend this class to control the order in which objects are processed.
Runtime location: <Windchill>/srclib/CommonComponents.jar
|
ObjectFormProcessor
|
Java interfacer
|
Classes implementing this interface perform the database and related tasks appropriate to the wizard using the form data. Each wizard will have only one ObjectFormProcessor class but multi-object wizards may have multiple instances of that class.
ObjectFormProcessors may call ObjectFormProcessor Delegates to perform subtasks.
Runtime location: <Windchill>/srclib/CommonComponents.jar
|
DefaultObjectFormProcessor
|
Java class
|
A default implementation of ObjectFormProcessor that contains the logic to execute ObjectFormProcessorDel egates and perform several other common tasks. This is the base class that should be extended by wizard-specific processors.
Runtime location: <Windchill>/srclib/CommonComponents.jar
|
ObjectFormProcessrDelegate
|
Java interface
|
Classes implementing this interface are called by ObjectFormProcessors to perform processing subtasks. Multiple ObjectFormProcessorDel egates may be called by one processor and the same delegate may be used by multiple processors to handle a task common to multiple wizards. These are optional.
Runtime location: <Windchill>/srclib/CommonComponents.jar
|
DefaultObjectFormProcessorDelegate
|
Java class
|
A default implementation of ObjectFormProcessorDelegate. This provides no-op behavior for methods a subclass may not need to implement. This is the base class that should be extended by task-specific delegates.
Runtime location: <Windchill>/srclib/CommonComponents.jar
|
ObjectBean
|
Java class
|
A container for the form data specific to a specific target object and the data common to all objects. Provides methods to retrieve the form data for a given object.
Runtime location:<Windchill>/srclib/CommonComponents.jar
|
ProcessorBean
|
Java class
|
A container for ObjectBeans that knows which ObjectBeans should be processed by the same processor instance and the order in which they should be processed.
Runtime location: <Windchill>/srclib/CommonComponents.jar
|
FormResult
|
Java class
|
A class used to pass method results between server methods and from the server to the WizardServet.
Runtime location: <Windchill>/srclib/CommonComponents.jar
|
The relationship between the main Java classes in the wizard processing framework is shown in the UML diagram below.
Form Processing Task Flow
Frequently, multiple tasks are performed on wizard target object(s) when processing the form data. These typically follow the following sequence:
1. Preprocessing
During this phase the setup for the database operation is done. For example:
◦ for an object creation wizard, instances of the object(s) are created and their attributes set from values in the form data
◦ for an object edit wizard, the object(s) are retrieved from the database and their attributes are modified per the values in the form data
2. Start a database transaction block
3. Do the database operation. For example:
◦ for an object creation wizard, the object(s) would be stored in the database
◦ for an object edit wizard, the object(s) would be updated in the database
4. Postprocessing
Do database or other tasks that need to be done after the main database operation but within the same transaction block so that the database operation will be rolled back if these task fail. These tasks typically require that the target object(s) be persisted beforehand. For example:
◦ share the object(s) to another container
◦ submit the object(s) to a workflow
5. End the database transaction block
6. Post-transaction processing
Do tasks that need to be done after the transaction block is closed so that the entire database transaction will not be rolled back if the tasks fail. For example:
◦ check out an object
Additional database operations may be performed in this phase if desired.
The tasks in each processing phase are performed by ObjectFormProcessors and ObjectFormProcessorDelegates. Every wizard must have an ObjectFormProcessor which should extend the DefaultObjectFormProcessor. This is the primary class that controls and performs the processing tasks appropriate to the wizard.
Wizards may or may not have one or more ObjectFormProcessorDelegates that are called by the ObjectFormProcessor (via the DefaultObjectFormProcessor) to perform processing subtasks. ObjectFormProcessorDelegates should extend the DefaultObjectFormProcessorDelegate class. See Create any necessary ObjectFormProcessorDelegate classes for your wizard for more information about ObjectFormProcessorDelegates.
Both ObjectFormProcessor and ObjectFormProcessorDelegate classes have preProcess(), doOperation(), postProcess(), and postTransactionProcess() methods for carrying out tasks during each processing phase.
| Wizard step validators are invoked when the user navigates wizard steps. To prepare the object on which the validation must be performed, the preprocess() method of the ObjectFormProcessor and ObjectFormProcessorDelegate classes will be executed. |
Not every wizard will have tasks in every phase, so it is not necessary that every processor and every delegate implement all of these methods, if they extend from the default classes DefaultObjectFormProcessor and DefaultObjectFormProcessorDelegate, respectively. Those parent classes provide default implementations of each method. If an ObjectFormProcessor does implement one of these methods, it should call the super() method of the DefaultObjectFormProcessor, which will handle the calling of ObjectFormProcessorDelegates.
The HTML form data will be passed to ObjectFormProcessors and ObjectFormProcessorDelegates in a list of ObjectBeans. An ObjectBean contains the data specific to one target object and the data common to all the target objects. For wizards with a single target object the list will contain only one ObjectBean. For wizards with multiple target objects the list will contain one ObjectBean for each target object and the ObjectBeans may be organized into a tree structure representative of the relationship between the objects and the order in which they should be processed. The creation of ObjectBeans is handled by the FormProcessorController.
The FormProcessorController also handles the opening, closing, and, if necessary, rollback of the database transaction and the calling of the ObjectFormProcessors. For the latter, the DefaultFormProcessorController uses objects called ProcessorBeans. Target objects with the same parent object, which are of the same type, and which have the same ObjectFormProcessorDelegates are placed into the same ProcessorBean.
Each ProcessorBean also will have its own instances of the ObjectFormProcessor and ObjectFormProcessorDelegates for the ObjectBeans it contains. ProcessorBeans may be organized into a tree structure to control the order in which objects are processed. (Wizards with a single target object will have only one ProcessorBean. )
The task flow of the DefaultFormProcessorController is as follows:
1. Call the preProcess() method of ObjectFormProcessor for the root ProcessorBean, passing it the ObjectBeans in the ProcessorBean. Then call the preProcess() method of the processors for the children of the root ProcessorBean, in the order in which they appear in the child list. Then call the preProcess() method for the children of the children, and so forth.
2. Do Transaction.start()
3. Call the doOperation() method of the ObjectFormProcessor for the root ProcessorBean and its children in the same way as preProcess().
4. Call the postProcess() method of the ObjectFormProcessor for the root ProcessorBean and its children in the same way as preProcess().
5. If steps 1-4 are successful, do Transaction.commit()
6. Call the postTransactionProcess() method of the ObjectFormProcessor for the root ProcessorBean and its children in the same way as preProcess().
If any method returns a status of FormProcessingStatus.FAILURE, the controller will call the setResultNextAction() method of the ObjectFormProcessor that failed so it can set information needed for the HTML response page.
| ObjectFormProcessors should not open/commit additional transaction blocks in steps 3 or 4 as nesting of transactions is not recommended. |
Wizards with Multiple Target Objects
Two types of multiple-object wizards are supported:
• wizards with multiple unrelated target objects
Example: create multiple parts
• wizards with a tree of related target objects
Example: create a change notice and related change tasks
The data specific to a given object should be contained either in a table row specific to that object or in a wizard step specific to that object. An example table where each row represents a part being created and each column is an attribute of the part is shown below.
Each wizard step must display one of these types of data:
• data in tabular format where each row represents a different object
• data that is specific to one and only one of the objects created
• data that is common to all objects created
A step cannot contain object-specific data for multiple objects unless it is in tabular format.
In multiple-object wizards, the object to which an input field applies is identified by an "objectHandle" embedded in the name attribute of the HTML input field. For example:
<input id="null1188140328133"
name="<someFieldIdString>!~objectHandle~newRowObj_430512131997223~!
<someAdditionalText>" value="" size="60" maxlength="60"
type="text">
In the example above, "newRowObj_430512131997223" is the object handle, "!~objectHandle~" is the required prefix, and "~!" is the required suffix. The HTML name attribute in which the object handle is embedded can be any string and the object handle may appear anywhere within the string.
When the DefaultFormProcessorController loads the form data in to ObjectBeans, it will strip off the object handle (including the required prefix and suffix) from the name attribute and use the resulting string as the key for the value in the form data parameter maps. For example, to retrieve the form value for the input field above you would call ObjectBean.getTextParameter() with the following key:
<someFieldIdString><someAdditionalText>
The framework generates object handles on name attributes for you in one of two ways:
• If data for the objects is being captured in tabular format, where each row represents an object and each column an attribute of the object, the handle will be dynamically generated for you if you set rowBasedObjectHandle=true on the table config in the builder::
table.setRowBasedObjectHandle(true);
The object handle for each row will be based on the row’s OID.
objectHandle = CreateAndEditWizBean.getNewObjectHandle(next.getOid().toString());
• Where all the data on a given wizard step is for the same object, you specify the object handle for that object on the wizard step action:
<jca:wizardStep action="setContextWizStep" type="object"
objectHandle="<your object handle string>" …
If data on the wizard step is common to all objects created, no object handle is needed on the input fields. The object handle associated to the data in an ObjectBean can be accessed by the ObjectBean.getObjectHandle() method. The FormProcessorController controls the order in which processors are called to process the ObjectBeans, as described in the sections below. Note that in the illustrations below, circles are used to represent ObjectBeans. Circles representing objects of the same type will have the same shading.
Multiple unrelated target objects
Typically, when a wizard has multiple unrelated target objects, the objects are the same type:
An example of such a wizard is that in which you can create multiple parts. This wizard has three steps:
1. Define Part
User enters the type of parts to be created and other attributes common to all the parts being created.
2. Set Attributes
User enters the name and number of each part to be created in tabular format. This table is dynamic in that the user can enter any number of parts.
3. Set Additional Attributes
User enters some additional attributes common to all of the parts.
If the user enters data for five parts, the DefaultObjectFormProcessorController will create five ObjectBeans. Each ObjectBean will contain the data from step 2 specific to the part it represents and the data from steps 1 and 3. Because there is no relationship between the parts and they can be created independently, none of the ObjectBeans will have parents or children. Since the same ObjectFormProcessor and ObjectFormProcessorDelegates will be used to process all the ObjectBeans and all the objects are of the same type, they will all be placed in the same ProcessorBean.
Multiple related target objects
Other wizards may have multiple related target objects. For example, you might have a wizard that creates a change notice and the change tasks related to that change notice. To create the associations between the change notice and the change tasks, the processor for the change notice will need to know how the objects relate to each other.
The change notice ObjectBean has three child ObjectBeans. The change task ObjectBeans have a parent ObjectBean and no children.
In this case, you would need to write your own FormProcessorController to create the structure of ObjectBeans. This can be a subclass of the DefaultFormProcessorController.
The default controller will create the ObjectBeans for you. You would override its createObjectBeanStructure() method, which is given a flat list of all the ObjectBeans. In that method you would set the parents and children of the ObjectBeans. You pass back a list of all the root ObjectBeans. After you have created the ObjectBean structure, the DefaultFormProcessorController will call the ProcessorBean.newCollection() method which will group the ObjectBeans into ProcessorBeans as follows:
In the diagram above the circles represent the ObjectBeans and the solid lines the relationships between them. The rectangles represent the two ProcessorBeans and the dotted line the relationship between them.
Each ProcessorBean will have its own instances of the ObjectFormProcessor and the ObjectFormProcessorDelegates needed for the objects in it. If the processor for the root ProcessorBean is called "ProcessorInstance1" and the processor for the child ProcessorBean is called "ProcessorInstance2", the processor methods would be called as follows:
| Method | Task Performed |
1 | ProcessorInstance1.preProcess(ObjectBean in Processor Bean 1) | create an instance of a WTChangeOrder2 and store it in the "object" attribute of the bean |
2 | ProcessorInstance2.preProcess(ObjectBeans in Processor Bean 2) | create three instances of WTChangeActivity2 and store them in the "object" attributes of the beans |
3 | ProcessorInstance1.doOperation(ObjectBean in Processor Bean 1) | persist the WTChangeOrder2 |
4 | ProcessorInstance2.doOperation(ObjectBeans in Processor Bean 2) | persist the WTChangeActivity2 instances |
5 | ProcessorInstance1.postProcess(ObjectBean in Processor Bean 1) | create the associations between the change notice and the change tasks |
6 | ProcessorInstance2.postProcess(ObjectBeans in Processor Bean 2) | none |
7 | ProcessorInstance1.postTransactionProcess(ObjectBean in Processor Bean 1) | none |
8 | ProcessorInstance2.postTransactionProcess(ObjectBeans in Processor Bean 2) | none |
The tasks could be arranged differently. For example, you could create the associations in method 6 instead of method 5 with the same effect. Or, you could create an ObjectFormProcessorDelegate to create the associations in its postProcess() method. The framework offers the flexibility to modularize your code as best fits your wizard. Just be sure to arrange your tasks correctly relative to the start and end of the main transaction.
Your structure of ObjectBeans could be more complex. For example:
As you can see from the diagram above, ObjectBeans will be placed in different ProcessorBeans if any of the following is true:
• the object in the ObjectBeans are different types
• the ObjectBeans have a different ObjectFormProcessor
(Note: at this time all the ObjectBeans in the wizard must have the same ObjectFormProcessor.)
• the ObjectBeans have a different list of ObjectFormProcessorDelegates
• the ObjectBeans have a different parent ObjectBean
The DefaultFormProcessorController will call the processors associated with each ProcessorBean starting at the root ProcessorBean and proceeding down the tree to the leaf ProcessorBeans.
Procedure - Creating Your Wizard Processing Code
You have two options in creating your wizard processing code:
• Wizards with a Single Target Object
• Wizards with Multiple Target Objects
Wizards with a Single Target Object
This process consists of the following steps:
• Create your processor class
• Specify the processor class for the wizard on the wizard action
• Create any necessary ObjectFormProcessorDelegate classes for your wizard
• Specifying the ObjectFormProcessorDelegate(s) to be used in your wizard
Create your processor class
Three processors for handling object creation and editing wizards are delivered with the product:
• com.ptc.core.components.forms.CreateObjectFormProcessor
• com.ptc.core.components.forms.DefaultEditFormProcessor
• com.ptc.core.components.forms.EditWorkableFormProcessor
These processors may be used as is or extended for your own purposes.
If your wizard is not an object creation or editing wizard you will need to create your own ObjectFormProcessor. ObjectFormProcessors should extend the DefaultObjectFormProcessor class.
You should place your form processing logic into the preProcess(), doOperation(), postProcess(), and postTransactionProcess() methods of the processor, as appropriate. Your methods should call the corresponding super method of the DefaultObjectFormProcessor, which will handle the calling of ObjectFormProcessorDelegates. These methods will be passed a single ObjectBean, which will contain all the form data from the wizard. The form data can be accessed using the getter methods of that object. The following getter methods are commonly used:
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)
See the javadoc for more information.
The "object" attribute of the ObjectBean represents an instance of the target object . Where appropriate, it should be set by one of your processor methods (most likely preProcess()) for use by downstream methods. Other information can be passed from one method to another using processor instance variables.
The NmCommandBean object passed to these methods contains information about the page from which the wizard was launched and the object selected on the parent page when the wizard was launched. It also contains all the HTML form data but you should use the methods on the ObjectBean rather than the NmCommandBean to access that data. See the javadoc for NmCommandBean for more information.
You pass the outcome of the preProcess(), doOperation(), postProcess(), and postTransactionProcess() methods back to the DefaultFormProcessorController using the com.ptc.core.component.FormResult object. Before returning, each of these methods should call FormResult.setStatus() to return the processing status. Three options are available:
• FormProcessingStatus.SUCCESS - if the method executed without error
• FormProcessingStatus.FAILURE - if the method encountered a fatal errors FormProcessingStatus.NON_FATAL_ERROR - if the method succeeded but encountered one or more problems that should be reported to the user.
The DefaultFormProcessorController passes the returned FormResult to its continueExecuting() method to determine whether processing should continue to the next phase or be aborted. By default, it will abort only if the status is FormProcessingStatus.FAILURE. If processing is to be aborted or after all processing competes successfully, the controller will call the setResultNextAction() method of the ObjectFormProcessor to set information in the FormResult needed to construct the response page sent back to the browser.
This method should convey the following information:
• The feedback messages should be displayed to the user, if any.
Determined from the feedbackMessages and exceptions variables.
Feedback messages, if any, are displayed before executing any window operations or provided javascript.
Exception messages are only displayed if status is FormProcessingStatus.FAILURE or FormProcessingStatus.NON_FATAL_ERROR.
See the javadoc for the FormResult and FormProcessingStatus classes for more information.
It is also possible for your ObjectFormProcessor's preProcess(), doOperation(), postProcess(), and postTransactionProcess() methods to throw exceptions. In that case, control will be returned to the ActionController which will set the variables of the FormResult as follows:
• status - FormProcessingStatus.FAILURE
• exceptions - the thrown Exception
This will cause the response page to display the exception message in an alert window and then close the wizard window.
How to get selected object(s) in form processor using NmCommandBean
There are four different form processing scenarios and to get the selected oid in each scenario there is different API on NmCommandBean that can be used. See the picture below for more details.
Specify the processor class for the wizard on the wizard action
You specify the ObjectFormProcessor class in the <command> subtag of the <action> tag for the wizard. Your action tag will be contained in a *actions.xml file.
Here is an example of how you would specify the CreateDocFormProcessor class as your processor.
<action name="create">
<command
class="com.ptc.core.components.forms.CreateObjectFormProcessor"
windowType="popup" />
</action>
Create any necessary ObjectFormProcessorDelegate classes for your wizard
ObjectFormProcessorDelegates may be used to carry out one or more subtasks in your wizard processing. Because the same delegate class may be called by multiple ObjectFormProcessors, they are typically used for tasks that are needed by multiple wizards. Here are some examples of how ObjectFormProcessorDelegates are used in the delivered product:
• Several object creation wizards have a checkbox named "Keep checked out after checkin." The ObjectFormProcessors for all these wizards call the same ObjectFormProcessorDelegate class to handle this checkbox and checkout the object after it is created if the box is checked.
• Wizards to create objects that are ContentHolders typically have a Set Attachments step to specific the documents that should be attached to the object. All these wizards call the same ObjectFormProcessorDelegate class to handle the persistence of the attachments.
• Many object creation wizards have an input field for a Location attribute to specify the object's folder. Because the processing of this input field is complex all these wizards call the same ObjectFormProcessorDelegate to set the folder on the object being created.
As shown in the examples above, a ObjectFormProcessorDelegate can handle the processing of one or many input fields. If your wizard does not have any HTML elements that are used in multiple wizards you may not need any delegates. However, they can be useful for modularizing your processing code also.
ObjectFormProcessorDelegates should extend the class DefaultObjectFormProcessorDelegate. ObjectFormProcessorDelegates are instantiated by the DefaultFormProcessorController and passed to the ObjectFormProcessor.
ObjectFormProcessorDelegates have preProcess(), doOperation(), postProcess(), and postTransactionProcess() methods just like ObjectFormProcessors. The delegates registered for the wizard will be called by the DefaultObjectFormProcessor during the different processing phases.
The outcome of an ObjectFormProcessorDelegate method is passed back to the ObjectFormProcessor using a FormResult, just as the ObjectFormProcessor methods pass back their results to the FormProcessorController.
Like ObjectFormProcessor methods, ObjectFormProcessorDelegate methods can throw exceptions. How these are handled depends on the ObjectFormProcessor calling it.
Specifying the ObjectFormProcessorDelegate(s) to be used in your wizard
The names of the ObjectFormProcessorDelegate classes to be instantiated by the DefaultObjectFormProcessor, if any, are communicated in hidden input fields as follows:
<input name="FormProcessorDelegate"
value="com.ptc.core.components.forms.NumberPropertyProcessor"
type="hidden">
These hidden input fields can be created in several ways:
• If you have a delegate associated with a specific wizard step, the delegate can be specified in the command subtag of in the wizard step action. For example:
<action name="attachmentsWizStep" postloadJS="preAttachmentsStep"
preloadWizardPage="false"
<command
class="com.ptc.windchill.enterprise.attachments.forms.Second
aryAttachmentsSubFormProcessor" windowType="wizard_step"/>
</action>
The wizard framework will then generate a hidden FormProcessorDelegate field in the wizard for any wizard using this step. If you have specified an object handle on the wizard step action, the name attribute of the hidden field will include the object handle so that the delegate will be called only for the target object associated with the step.
• If you have a specific input field that requires a delegate you can generate the hidden field in the data utility that creates the GUI component for the input field. It is recommended that the data utility return a subclass of AbstractGuiComponent to take advantage of the addHiddenField() method and the AbstractRenderer. After creating the GUI component in the data utility, call the method addHiddenField() in the AbstractGuiComponent class. For example:
LocationInputGuiComponent guiComponent = new
LocationInputComponent(…);
guiComponent.addHiddenField
(CreateAndEditWizBean.FORM_PROCESSOR_DELEGATE, "com.
ptc.windchill.enterprise.folder.LocationPropertyProcess
or");
The hidden input field will be generated for you by the AbstractRenderer automatically. If the field is associated with a step or table row that has an object handle, that object handle will be embedded in the HTML name attribute of the hidden field. If you choose to return a GUI component that does not extend AbstractGuiComponent, your GUI component and renderer would have to know how to render the necessary hidden field.
• You can include a hidden field for your delegate directly in your jsp file. However, one of the first two methods is preferred because it encapsulates the hidden field with its associated HTML input fields.
Wizards with Multiple Target Objects
This process consists of the following steps:
• Create your processor class
• Specify the processor class for the wizard on the wizard action
• Create any necessary ObjectFormProcessorDelegate classes for your wizard
• Specifying the ObjectFormProcessorDelegate(s) to be used in your wizard
• Create a FormProcessorController, if needed
• Specify the FormProcessorController to be used in your wizard, if needed
Create your processor class
The same as for a wizard with a single target object. See Create your processor class.
Specify the processor class for the wizard on the wizard action
The same as for a wizard with a single target object. See Specify the processor class for the wizard on the wizard action.
Create any necessary ObjectFormProcessorDelegate classes for your wizard
The same as for a wizard with a single target object. See Create any necessary ObjectFormProcessorDelegate classes for your wizard.
Specify the ObjectFormProcessorDelegate(s) to be used in your wizard
The same as for a wizard with a single target object. See Specifying the ObjectFormProcessorDelegate(s) to be used in your wizard. Remember that an object handle must be embedded in the name attributes of the hidden fields specifying the delegates if you want the delegate to be used only for a given object.
Create a FormProcessorController, if needed
If the target objects of your wizard form a structure of related objects, you will need to create your own FormProcessorController to create a structure of the ObjectBeans. You should subclass the DefaultFormProcessorController to leverage its ability to partition the form data into ObjectBeans and ProcessorBeans and call the form processors. Typically, the only method you will need to override is the createObjectBeanStructure() method.
Specify the FormProcessorController to be used in your wizard, if needed
If you have created your own FormProcessorController, you should specify the controller to be used for your wizard on the wizard tag in your main JSP file for the wizard. For example:
<jca:wizard helpSelectorKey="change_createProblemReport"
buttonList="DefaultWizardButtonsWithSubmitPrompt"
formProcessorController="com.ptc.windchill.enterprise.change2.forms.
controllers.ChangeItemFormProcessorController">
FormResult / Client-Side Post Form Processing
Starting with the Windchill 10.1 MR010 release, the form processing and the action response handling have been separated. This means that the form processor will still perform the work to update/add/delete the selected/affected objects, but there is no need to specify the next action on the FormResult. The FormResult will contain the OIDs of the objects that were updated/added/deleted. These OIDs will be passed back to the client and the client will update the components that are currently displaying those objects.
Setting up the FormResult
Starting with the Windchill 10.1 MR010 release there is no need to set the next action (i.e. setNextAction(FormResultAction.REFRESH_OPENER)). Only the OIDs the objects added/updated/removed should be added to the FormResult object.
Example: formResult.addDynamicRefreshInfo new DynamicRefreshInfo(newOid, oldOid, NmCommandBean.DYNAMIC_UPD))
The ability to set a URL or Javascript function on the FormResult is still valid. However, this should only be done when no other options are available. Because multiple clients are potentially invoking these actions the URL javascript returned may not be appropriate or may not reference valid code. Ideally, you want the components to update themselves and not have individual actions update the components on the page. If a URL is set on the FormResult, the afteraction listener will not be called on the component where the action was launched from. However, the objectsaffectedlistener will be called.
Refreshing/Updating JCA Components
There are two listeners that can be attached to components for objects updated via actions. Most of the components (i.e. table, tree, info page, etc.) have a default listener for both of the events.
• afteraction : This listener is responsible for updating the objects in the component where the action was launched/executed from. For example, if the checkout action was executed from the information page, the information page afteraction listener is responsible for refreshing itself. Likewise, if the checkout action was launched from a table, the table afteraction listener will be responsible for updating the row in the table. This listener can be attached to any Ext component by adding the afteraction event listener. The listener will receive the FormResult JS object as a parameter.component.on(‘afteraction’, someAfterActionListener);
• objectsaffected : This listener is responsible for updating the objects in a component when the action is executed from a different component. For example, if the checkout action was performed from the information page and the search results table contained that same object, the objectsaffected listener for the [search results] table will be responsible for updating the row in the table. This listener is attached to the global PTC.action object: component.mon(PTC.action, ‘objectsaffected’,someListener);
Examples of Custom Action Handling
Refreshing an entire component when any action occurs inside of it
me.on('afteraction', me.onAfterAction);
me.onAfterAction = function(formResult) {
me.refresh();
return true;//Stop further processing
};
Replacement for FormResultAction.FORWARD and LOAD_OPENER_URL
Different components have different behavior and different client platforms have different URL patterns so it is not supported to generically try and specify a URL on the FormResult. Each component can decide how to do it individually and the afterAction listeners on common components such as the infopage and miniNavigator already have generic logic to refresh themselves. The miniNavigator for example does not want to forward to the new URL because that would break the user out of the current context and so it refreshes itself instead.
See the checkout example below for how to have a component specific event listener do something unique. See the PTC.miniNavigator.onAfterAction and the PTC.infoPage.onAfterAction code in the js files. If extra URL parameters are needed to be passed down to construct the url on the client side then the FormResult.extraData map can be used to pass down extra information.
You can change how an object is handled for a specific table. For example add a row to the checkouts table instead of adding a checkout glyph to an existing row (from 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);
Replacements for FormResultAction.JAVASCRIPT
In cases where extra javascript was needed to run to update specific components in the page as the result of an action, it is better to add component specific objectsaffected listeners instead that react to the specific action, such as the checkout action example below. Some wizards may want to add a listener specific to the wizard because the javascript is often times not needed in other pages.
Wizard Specific Handler
Actions that need to perform custom or specific code as the result of a wizard can use a callback function for handling a successful submission. This successFunc configuration parameter is passed to the PTC.wizard.submitWizard function. This successFunc is then called when the wizard receives the FormResult from the server and begins to fire the afteraction and objectsaffected events. This allows an action owner to control the flow and behavior of the wizard more specifically than needing to modify how individual components handle the event name. For Example, the checkin Button on the edit wizard has a success handler to launch the checkin wizard once the attributes are successfully saved to the database.
In the sample code below, notice that the function passed to the wizard code:
<command onClick="onEditSubmit('checkinButton')"/>
function onEditSubmit(actionName){
var params = {successFunc: PTC.wizard.launchCheckinWizard,
finished: actionName=='saveButton'};
PTC.wizard.submitWizard(params);
}
Change how an object is handled for a specific table
For example, you can add a row to the checkouts table instead of adding a checkout glyph to an existing row (from 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);
Replacement for FormResultAction.FORWARD and LOAD_OPENER_URL
Different components have different behavior and different client platforms have different URL patterns so it’s not supported to generically try and specify a URL on the FormResult. Each component can decide how to do it individually and the afterAction listeners on common components such as the infopage and miniNavigator already have generic logic to refresh themselves. The miniNavigator for example does not want to forward to the new URL because that would break the user out of the current context and so it refreshes itself instead.
See the checkout example below for how to have a component specific event listener do something unique. Also see the PTC.miniNavigator.onAfterAction and the PTC.infoPage.onAfterAction code in the js files. If extra URL parameters are needed to be passed down to construct the url on the client side then the FormResult.extraData map can be used to pass down extra information. In most cases it is not necessary to auto-forward to the info page of an object just created or modified. After create actions, an inline message appears and contains a link to allow someone to go to the new objects info page. This is how one might auto-forward after an action. Modify the component’s onAfterAction method to include this code sequence:
if (formResult.actionName === 'checkout') {
PTC.infoPage.goTo(formResult.getUpdatedOids()[0]);
}
return true;
You can add a generic action handler to all possible components that will auto-forward for a specific action. The following code example details this. However, this is not necessarily recommended because some pages and components might lose in-progress data.
PTC.action.on('objectsaffected', function (formResult) {
if (formResult.actionName === 'checkout') {
PTC.infoPage.goTo(formResult.getUpdatedOids()[0]);
}
return true;
});
Limitations
• Only one ObjectFormProcessor class per wizard is supported at this time
• The framework does not support having data for multiple objects in the same wizard step unless it is in tabular format. It also does not support having data common to all the objects on the same step as object-specific data.
Additional Resources
Related Package/Class 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