Custom <af:train> model
The <af:train> component presents a series of 'stops', each representing a task in a multi-step process, carried out sequentially. This component is very useful when you need to develop a 'wizard' sort of screens.
In general, af:train is used along with taskflow where the implementation part is a cakewalk (can be done declaratively with zero effort). Sometimes you may need to customize the default behavior of the train component. ADF Faces does support this customization through the custom menu model. To learn more about this, please go through chapter 18.7 Using Train Components to Create Navigation Items for a Multi-Step Process of Web User Interface Developer's Guide.
I'm attaching a simple af:train demo using customized org.apache.myfaces.trinidad.model.ProcessMenuModel as model. This is actually a simplified version of the af:train component sample, bundled with ADF Faces Rich Client Components Demo.
A brief description of this implementation...
jspx tag snippet is shown below
Class diagram for custom menu model
Custom menu model used for building this sample is TrainIdMenuModel class. This is subclassed from org.apache.myfaces.trinidad.model.ProcessMenuModel. Initialization logic is copied below for your reference. Please note the usage of @PostConstruct in this bean to perform the initialization.
@PostConstruct: Specifies a method that the container will invoke after resource injection is complete but before any of the component's life-cycle methods are called.
You can download the af:train sample using custom menu model from here.
Adding listeners for each train stop
Sometimes, you may need to invoke custom actions while navigating through train stops. The below sample provides an interesting approach for this requirement. Idea is to enhance NavigationItemId's doActionListener() method, to trigger the registered action listeners. Below sample implements this use case(Please note that, this sample is just intended to give you an idea on this topic).
You can download the af:train sample using custom menu model+custom action from here.
In general, af:train is used along with taskflow where the implementation part is a cakewalk (can be done declaratively with zero effort). Sometimes you may need to customize the default behavior of the train component. ADF Faces does support this customization through the custom menu model. To learn more about this, please go through chapter 18.7 Using Train Components to Create Navigation Items for a Multi-Step Process of Web User Interface Developer's Guide.
I'm attaching a simple af:train demo using customized org.apache.myfaces.trinidad.model.ProcessMenuModel as model. This is actually a simplified version of the af:train component sample, bundled with ADF Faces Rich Client Components Demo.
A brief description of this implementation...
jspx tag snippet is shown below
<af:train var="node" value="#{viewScope.CustomTrainModel}"
id="train">
<f:facet name="nodeStamp">
<af:commandNavigationItem text="#{node.label}"
actionListener="#{node.doActionListener}"
immediate="#{viewScope.CustomTrainModel.immediate}"
disabled="#{viewScope.CustomTrainModel.readOnly}"
visited="#{viewScope.CustomTrainModel.visited}"
id="commandNavigationItem"/>
</f:facet>
</af:train>
Class diagram for custom menu model
Custom menu model used for building this sample is TrainIdMenuModel class. This is subclassed from org.apache.myfaces.trinidad.model.ProcessMenuModel. Initialization logic is copied below for your reference. Please note the usage of @PostConstruct in this bean to perform the initialization.
@PostConstruct: Specifies a method that the container will invoke after resource injection is complete but before any of the component's life-cycle methods are called.
@PostConstruct
public void initilize() {
/**
* Gets the nodes for the train
*/
List<NavigationItemId> viewEntries =
ServiceProxy.getInsatnce().getSomeDummyNavigationItemsToTest();
ChildPropertyTreeModel childPropertyTreeModel =
new ChildPropertyTreeModel();
childPropertyTreeModel.setChildProperty("children");
childPropertyTreeModel.setWrappedData(viewEntries);
this.setViewIdProperty("id");
this.setWrappedData(childPropertyTreeModel);
this.setIdKey("idTrainViewIdKey");
}
You can download the af:train sample using custom menu model from here.
Adding listeners for each train stop
Sometimes, you may need to invoke custom actions while navigating through train stops. The below sample provides an interesting approach for this requirement. Idea is to enhance NavigationItemId's doActionListener() method, to trigger the registered action listeners. Below sample implements this use case(Please note that, this sample is just intended to give you an idea on this topic).
You can download the af:train sample using custom menu model+custom action from here.
Hi Jobinesh,
ReplyDeleteIt's a useful Post. I was trying to implement a simple Train Component in ADF using JDEV 11g and I am getting the following warning in the console:
Either a MenuModel object was not provided or an invalid object was provided.
Either a MenuModel object was not provided or an invalid object was provided.
Also, the train component is not displayed on the page.
It looks like I am hitting the following error:
ADF_FACES-30060: Either a MenuModel object was not provided or an invalid object was provided.
Cause: The train component required a value of type MenuModel.
Action: Correct the train definition.
But I don;t know where to fix it.
Here is my jspx code snippet.
f:view
af:document id="d1"
af:messages id="m1"
af:form id="f1"
af:train value="#{controllerContext.currentViewPort.taskFlowContext.trainModel}"
id="t1"
af:trainButtonBar value="#{controllerContext.currentViewPort.taskFlowContext.trainModel}"
id="tbb1"
af:panelStretchLayout id="psl1"
Any help will be appreciated.
Thanks,
Siva
Siva,
ReplyDeleteAre you trying to use it as part of an an unbounded task flow?Look like that's the error here
Thanks for your kind reply.
ReplyDeleteNo, It's a bounded flow.
Here is the code.
xml version="1.0" encoding="windows-1252" ?>
adfc-config xmlns="http://xmlns.oracle.com/adf/controller" version="1.2">
task-flow-definition id="Train">
default-activity id="__1">Emp
view id="Emp">
page>/Emp.jspx
train-stop id="__2"/>
view>
view id="Address">
train-stop id="__3"/>
view>
train/>
task-flow-definition>
adfc-config>
Thanks,
Siva
Page defn for view id="Address" is missing?
ReplyDeleteThanks again.
ReplyDeleteI created the page, but still I am getting the same error.
?xml version="1.0" encoding="windows-1252" ?>
adfc-config xmlns="http://xmlns.oracle.com/adf/controller" version="1.2">
task-flow-definition id="Train">
default-activity id="__1">Emp
view id="Emp">
page>/Emp.jspx
train-stop id="__2"/>
view>
view id="Address">
page>/Addr.jspx
train-stop id="__3"/>
view>
train/>
task-flow-definition>
adfc-config>
Would be great if you can send me a test app, will take a look - jobinesh@gmail.com
ReplyDeleteThanks for looking into this.
ReplyDeleteI have sent you the .EAR file.
Thanks again.
Siva
Thank you Jobinesh for your help. As you indicated in the email, I was running an individual page, not the task-flow xml. Tried the task flow xml and its working fine.
ReplyDeleteHi Jobinesh,
ReplyDeleteThis was a great post and this just suits my needs of dynamically creating train model.
One question though, how can I generate a UI on each of the train stops?
Since it will be dynamic then how can I add ADF Faces components on each trainstops?
I am thinking of having a fixed sets of taskflow and I would like to dynamically bind each taskflow on each trainstops?
What could be the best approach given your code?
Thanks a lot.
Regards,
Phu Tek
This comment has been removed by the author.
ReplyDeleteThis comment has been removed by the author.
ReplyDeleteBlogger might be blocking my code...
ReplyDeleteaf:trainButtonBar value="#{viewScope.CustomTrainModel}"
id="tbb1"/>
sorry for spamming your blog post..
Hi Jobinesh,
ReplyDeleteJust to add also.
Why is it not possible to add a train buton bar component on your code?
I tried it with this one.
af:train var="node" value="#{viewScope.CustomTrainModel}"
id="train">
f:facet name="nodeStamp">
af:commandNavigationItem text="#{node.label}"
actionListener="#{node.doActionListener}"
immediate="#{viewScope.CustomTrainModel.immediate}"
disabled="#{viewScope.CustomTrainModel.readOnly}"
visited="#{viewScope.CustomTrainModel.visited}"
id="commandNavigationItem"/>
af:trainButtonBar value="#{viewScope.CustomTrainModel}"
id="tbb1"/>
..but I am unable to navigate the Prev and Next button?
Regards,
Phuu Tek
Phu Tek,
ReplyDeleteDrop me a mail with the sample you tried, will take a look
Hi Jobinesh,
ReplyDeleteI already sent an email to your account.
Thank You!
Hi Jobinesh,
ReplyDeleteI think I am making headway on how to implement my use case. Thanks to your post.
But I just would like to know if given your code above, is it possible to set that a particular navigation item is Sequential/Skip/Ignore?
Similar to setting of a train-stop?
Thanks
Hi Jobinesh,
ReplyDeleteInteresting way to create a custom train model.
However, I notice that when I am on Step 5 and I go back to Step 1, I need to go over to Step 2-4.
Isn't it possible such that when I am on the last step, I could navigate to earlier stops without navigating sequentially?
I mean, when I have visited a trainstop already then it should be visited always?
How to do that given this implementation.
Regards,
Rakesh