Flow depending on external parameter? - jsf

I'm building a simple POC to experiment with Faces Flow.
page 1: displays a list of companies. The user selects company A then goes to page 2.
page 2: on the selected company page, the user clicks a commandLink to start a wizard to create a new employee to be added to the company A.
Behing the scenes I've got a #FlowScoped("addNewUsertoCompanyFlow") bean MyFlowBean.
In its #PostConstruct method, the MyFlowBean needs to fetch an object corresponding to the company A from a service (#Inject).
What's the right way to let MyFlowBean know about the ID of the company A so that it can fetch it from the service ?
Thanks.

Ok, I came up with a solution. The key was not to use the flow backing bean #PostConstruct but rather use the flow initializer, where I can grab request parameters.
So I'm using some additional input in the form that will start my flow:
<h:form id="myForm" prependId="false">
<h:commandLink value="Enter myFlow" action="my-flow"/>
<h:inputHidden id="parameter" name="parameter" value="8"/>
</h:form>
In my flow definition I've defined an initializer for the flow, calling some method in the flow backing bean
#Produces #FlowDefinition
public Flow defineFlow(#FlowBuilderParameter FlowBuilder flowBuilder) {
String flowId = "my-flow";
flowBuilder.id("", flowId);
flowBuilder.initializer("#{myFlowBean.startFlow()}");
...
}
I've then grabbed the parameter inside the backing bean.
#Named
#FlowScoped("my-flow")
public class MyFlowBean implements Serializable {
public void startFlow() {
String parameter = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("parameter");
//now do sthg with the parameter, such as fetching data from an injected service
....
}
}
Of course it's also possible to do that at the flow definition level
flowBuilder.initializer("#{trainingFlowBean.startFlow(param['parameter'])}");
and just have a parameter in the startFlow method
public void startFlow(String parameter) {
...
}

Related

How to hold values in a CDI conversion scope?

I try to add a object to a LinkedList in a #ConversationScoped backing bean.
#Named
#ConversationScoped
public class CategoryController implements Serializable{
...
private List<Category> selectedCategories = new LinkedList<Category>();
...
#PostConstruct
public void initNewMember() {
conversation.begin();
newCategory = new Category();
loadExistingCategories();
}
...
}
I want to send an ajax request with new objects (of type category). They should simply be added to the linked list.
this.selectedCategories.add(newParentCategory);
With the ajax render attribute <f:ajax render="allSelectedCategories"/> I immediately render the output text to render the object list.
<h:outputText id="allSelectedCategories" value="#{categoryController.selectedCategories}" />
And yes, the object I clicked is displayed, but the previously clicked objects are gone.
The values do not serialize/persist in memory during my "conversation". What do I need to do to make that conversion scope temporarily persist the values of the ajax calls?
I really want to get used to CDI and abandon the ManagedBean path for this project (e.g. #ViewScoped), despite the fact that it works like a charm.
Also, I cannot reproduce the following tutorial on CDI Conversation Scope. I simply cannot debug into the initConversation by adding
<f:event listener="#{categoryController.initConversation}"
type="preRenderView"></f:event>

JSF redirect to same URL with different parameters

I have the following site structure:
web
|_events
|_view.xhtml
|_news
|_view.xhtml
|_pages
|_view.xhtml
...
When a user clicks a link to, let's say, Event #4, he will be redirected to e.g. http://example.com/events/view.xhtml?itemId=4On the event->view.xhtml page I have a menu with links to some other events (somehow related to the event that is currently viewed). Let's say those are Event #1 and Event #2.
Currently my menu links have value="../event/view.xhtml?itemId=#{row.id}". When it's clicked, the user will be redirected to e.g. http://example.com/events/view.xhtml?itemId=2
But if I use this approach (with "../event/" in the URL), it means that I have to create two more link types, with URLs for news and for pages. And also for every other item type that I make...
Is there any way to put only "itemId=X", i.e. only the new parameter value in the link value? Then I could use one menu template for every item type on my site.
Thanks!
You could use a List in ApplicationScope or SessionScope according to your use case and add all the type there.
Something like
public class Type implements Serializable {
private String name;
private List<Integer> items;
//getters and setters
}
Managed Bean
public class TestBean {
private List<Type> types;
#PostConstruct
public void init(){
//constructTypesAccordingToSomeLogic();
}
//getters and setter
}
View
<ui:repeat value="#{testBean.types}" var="type">
<ui:repeat value="#{type.items}" var="item">
<h:outputLink value="../#{type.name}.xhtml?itemId=#{item}"></h:outpuLink>
</ui:repeat>
</ui:repeat>

Constructor of CDI managed bean is invoked twice while opening the page

I am trying to use the ChartBean sample from PrimeFaces. This is the view:
<h:form>
<p:layoutUnit position="center">
<p:lineChart id="linear" value="#{chartBean.linearModel}" legendPosition="e"
title="Linear Chart" minY="0" maxY="1000" style="height:600px"/>
</p:layoutUnit>
</h:form>
This is the bean:
#Named
#RequestScoped
public class ChartBean implements Serializable {
private CartesianChartModel categoryModel;
private CartesianChartModel linearModel;
public ChartBean() {
System.out.println("ChartBean constructed");
createCategoryModel();
createLinearModel();
}
// ...
}
While I run it, I noticed that the constructor of this bean is invoked twice while opening the page. The logs shows the following:
INFO: ChartBean constructed
INFO: ChartBean constructed
So the bean was instantiated twice. How is this caused and how can I avoid that? I'm interacting with the DB to get some data to display in the UI and this way the data is unnecessarily fetched twice.
The first creation is the container creating a scoped proxy of your bean. The scoped proxy is an object that extends your bean and gets injected whenever some other component needs your bean. It's methods however do not execute the real logic, rather delegate their execution to the correct contectual instance of your bean. An example will clarify:
Assume 2 requests, R1, R2. There must be 2 instances of ChartBean, B1 and B2. Say another component, C, depends on ChartBean. The relevant field of C must be injected with an instance of ChartBean at application init time and call the correct bean instance at execution time. BUT at app init time there is no request and certainly no B1, B2. What does the container do? It creates the scoped proxy and injects it to whoever needs it. Then, whenever ChartBean.method() is called, it is called on the proxy who decides which is the correct bean to call (B1 for R1, B2 for R2, throw exception if no request is active, e.g. called from a MessageDrivenBean).
Having said the previous: DO NOT RUN BUSINESS LOGIC IN CONSTRUCTORS IN JAVA EE COMPONENTS, because constructors may be called from the system/container. Use a #PostConstruct method instead:
...
public class ChartBean implements Serializable {
public ChartBean() {
// only construction logic here
}
...
#PostConstruct
void postConstruct() {
createCategoryModel();
createLinearModel();
}
}
By the way you can verify that the constructor is called from a proxy implementation by printing the class name in the constructor:
public ChartBean() {
System.out.println("ChartBean as " + this.getClass().getName());
}
The first time it gets called, it will be some other class than your own.

Single page applications with ajax [duplicate]

This question already has answers here:
How to ajax-refresh dynamic include content by navigation menu? (JSF SPA)
(3 answers)
Closed 1 year ago.
I'm relatively new to JSF and trying to learn how current JSF 2 applications are designed. I've seen reference to single page applications that use ajax. Can someone fill me in on some of the techniques used and / or point me to a model or book? The books I've seen (JSF Complete Reference etc.) are good for basic tech issues but I can't find a source for current design techniques.
Thanks
Dave
In order to implement your Single Page Application, you should state which piece of your page should be rendered. This can be accomplished making use of a boolean flag such as create, edit, list, and so on. For instance, see the following (Just relevant code)
<h:body>
<h:form rendered="#{userController.stateManager.create}">
<h:panelGroup rendered="#{not empty facesContext.messageList or userController.stateManager.failure}">
<!--render error message right here-->
</h:panelGroup>
<div>
<label>#{messages['br.com.spa.domain.model.User.name']}</label>
<h:inputText value="#{user.name}"/>
</div>
<h:commandButton action="#{userController.create}">
<f:ajax execute="#form" render="#all"/>
<f:actionListener type="br.com.spa.web.faces.listener.StateManagerActionListener" />
<f:setPropertyActionListener target="#{userController.stateManager.create}" value="true"/>
<f:setPropertyActionListener target="#{userController.user}" value="#{user}" />
</h:commandButton>
</form>
</h:body>
Notice that our form will be rendered when a flag create is true - See second line above. To wrap our flags, we create a classe named StateManager as follows
/**
* I am using lombok, which takes care of generating our getters and setters. For more info, please refer http://projectlombok.org/features/index.html
*/
#Setter #Getter
public class StateManager {
private boolean create;
private boolean edit;
private boolean list;
}
Now, because we are using only a single page, we should use a ViewScoped managed bean, which keep our managed bean scoped active as long as you are on the same view - Is it a single page application, right ? So, no navigation. With this in mind, let's create our managed bean.
#ManagedBean
#ViewScoped
public class UserController implements StateManagerAwareManagedBean {
private #Inject UserService service;
private #Getter #Setter stateManager = new StateManager();
private #Getter #Setter List<User> userList = new ArrayList<User>();
private #Getter #Setter User user;
#PostConstruct
public void initialize() {
list();
}
public void create() {
service.persist(user);
stateManager.setCreate(false);
stateManager.setList(true);
stateManager.setSuccess(true);
}
public void edit() {
service.merge(user);
stateManager.setEdit(false);
stateManager.setList(true);
stateManager.setSuccess(true);
}
public void list() {
userList = service.list();
stateManager.setList(true);
}
}
For each action method, we define which piece of our page should be rendered. For instance, consider that our form was processed, covering all of JSF lyfecycle, which implies that their values was successfully converted and validated, and our action method invoked. By using as example our create action method - see above -, we set its create flag as false because our form was converted and validated, so we do not need to show it again (Unless you want). Furthermore, we set both list and success flag as true, which indicates that the list of our page should be rendered and our form was successfully processed - You could use this flag to show something like "User created" such as bellow
<h:panelGroup rendered="#{userController.stateManager.success}">
#{messages['default.created.message']}
</h:panelGroup>
Now, let's discuss which piece of our page should be rendered when it is called for the first time. Maybe you do not know but a void method annotated with #PostConstruct will be called first. So we define which piece of our page should be rendered. In our example, we call list method, which sets its list flag as true and populate a backing list.
#PostConstruct
public void initialize() {
list();
}
Finally, let's review the following order nested within h:commandButton
<h:commandButton action="#{userController.create}">
<f:ajax execute="#form" render="#all"/>
<f:actionListener type="br.com.spa.web.faces.listener.StateManagerActionListener" />
<f:setPropertyActionListener target="#{userController.stateManager.create}" value="true"/>
<f:setPropertyActionListener target="#{userController.user}" value="#{user}" />
</h:commandButton>
First of all, you should call an ActionListener - here called StateManagerActionListener - which takes care of resetting any StateManager - code bellow. It must be called first before any other setPropertyActionListener designed to control any flag because the order defined within h:commandButton is the order in which they will be called. keep this in mind.
public class StateManagerActionListener implements ActionListener {
public void processAction(ActionEvent e) throws AbortProcessingException {
Map<String,Object> viewMap = FacesContext.getCurrentInstance().getViewRoot().getViewMap();
for(Map.Entry<String,Object> entry: viewMap.entrySet()) {
if(entry.getValue() instanceof StateManagerAwareManagedBean) {
((StateManagerAwareManagedBean) entry.getValue()).setStateManager(new StateManager());
}
}
}
}
StateManagerAwareManagedBean - used in our ViewScoped Managed bean -, which allows that we reset any StateManager of any ManagedBean instead of resetting one by one in our ActionListener, is defined as follows
public interface StateManagerAwareManagedBean {
StateManager getStateManager();
void setStateManager(StateManager stateManager);
}
Second, after defining our ActionListener, we use a setPropertyActionListener which set the flag which controls the enclosing piece of the view as true. It is needed because our form is supposed to be not converted and validated. So, in our action method, we set this flag as false as discussed before.
A couple of notes
User is marked as a RequestScoped ManagedBean so that it can not be injected into a ViewScoped one using a ManagedProperty because its scope is shother. To overcome this issue, i set its value by using a <f:setPropertyActionListener target="#{userController.user}" value="#{user}"> - See our form
Our example use JEE features which need a proper Application Server. For more info, refer http://docs.oracle.com/javaee/6/tutorial/doc/
ManagedBean can play different roles such as a Controller, DTO and so on. When it play a role of a Controller, i prefer suffix its name with Controller. For more info, refer http://java.dzone.com/articles/making-distinctions-between

Unable to retrieve the value of <h:selectBooleanCheckbox> inside my managed bean's action method

JSF view code:
<f:view>
<h:form>
<h:panelGrid>
<h:inputText id="key" value="#{myManagedBean.key}"/>
<h:selectBooleanCheckbox id="rerun" value="#{myManagedBean.rerun}" rendered="#{myManagedBean.displayRerun}"/>
<h:commandButton id="check" action="#{myManagedBean.check}"/>
</h:panelGrid>
</h:form>
<f:view>
JSF model code:
public class MyManagedBean {
private boolean displayRerun;
public void setDisplayRerun(boolean aDisplayRerun) {
this.displayRerun = aDisplayRerun }
public boolean getDisplayRerun() {
return this.displayRerun;
}
private String key;
public void setKey(String aKey) {
this.key = aKey
}
public String getKey() {
return this.key;
}
private boolean rerun;
public void setRerun(boolean arerun) {
this.rerun = arerun
}
public boolean getRerun() {
return this.rerun;
}
public String check() {
//do data validation
setDisplayRerun(true);
System.out.println(getRerun());
}
}
This always prints false regardless of whether the checkbox is checked or not.
Additional Information on my requirement:
Nick/BalusC, my managed bean is of request scope. It is indeed simplified code snippet that I presented. My page has couple of user input controls along with a command button. On submit of command button, I call action method of backing bean, in which I do data validation (in this case I lookup database and see if the inputs are already registered.) If already registered, I come back to the same page, this is when I display the singleBooleanCheckBox for the user to select and hit the command button again.
I am toggling the display of the checkbox based on a managedbean property (a boolean flag set during data validation).
When I re-submit the page with checkbox checked, I do not receive this data.
For further verification, I replace the selectBooleanCheckbox, with a command button with similar behavior (basically do not render it initially, but only show it on data validation). I mapped its #action to my managedbean's action method. To my surprise, when I hit the button, the action method is not executed. Instead, the page is refreshed like in a "immediate" scenario or a redirect.
I have been struggling on this for almost 6 hrs. Appreciate your experienced insights.
Thanks.
So, you've actually a rendered attribute on the checkbox (that was not present in your initial question) and the bean is request scoped (it would have worked when it was session scoped). The submitted checkbox value will not be gathered during apply request values phase when this attribtue evaluates false at that point.
You basically need to retain the condition responsible for the rendered attribute in the subsequent request as well. This can be done in several ways: putting bean in session scope, using Tomahawk's t:saveState or h:inputHidden with a bean binding. Each is outlined in detail in this answer.

Resources