JSF commandButton 'strange' behaviour - jsf

I'll be very pleased to get a decent explaination for the following case.
Here's a simple JSF with two forms and text output:
<h:body>
<h:form>
<h:commandButton value="Go" action="#{wierdBean.doWierdStuff}"/>
</h:form>
<h:form>
<h:dataTable value="#{wierdBean.pages}" var="page">
<h:column>
<h:commandButton value="the same go action?" action="#{wierdBean.doWierdStuff}"/>
</h:column>
</h:dataTable>
</h:form>
</h:body>
<h:dataTable value="#{wierdBean.pages}" var="page">
<h:column>
<h:outputText value="#{page}"/>
</h:column>
</h:dataTable>
'Go' button at the top is supposed to do the same thing
as the 'the same go action?' buttons.
Backing WierdBean is:
public class WierdBean implements Serializable {
private int buttonsCount;
public WierdBean() {
System.out.println("WierdBean()");
}
#PostConstruct
public void postConstruct() {
System.out.println("postConstruct()");
}
public Integer[] getPages() {
System.out.print("getPages() buttonsCount(): " + buttonsCount);
Integer[] pages = new Integer[buttonsCount];
for (int i = 0; i < pages.length; i++) {
pages[i] = new Integer(i);
}
return pages;
}
public String doWierdStuff() {
System.out.println("doWierdStuff()");
buttonsCount = 2;
return "wierd";
}
}
When I enter the page I get:
INFO: WierdBean()
INFO: postConstruct()
INFO: getPages() buttonsCount(): 0 (16 times)
and I seen only the 'Go' button. That's understandable.
After Pressing the 'Go' button I get:
INFO: WierdBean()
INFO: postConstruct()
INFO: getPages() buttonsCount(): 0 (19 times)
INFO: doWierdStuff()
INFO: getPages() buttonsCount(): 2 (16 times)
Nice, doWierdStuff is called and then I get 2 'the same go action' buttons and 2 text outputs.
That's fine.
However, when I press any of the the 'the same action' buttons, which are supposed to do the
same thing as 'Go' button - call the doWierdStuff method - I get:
INFO: WierdBean()
INFO: postConstruct()
INFO: getPages() buttonsCount(): 0 (44 times)
There is only 'Go' button visible.
Why is that?

Your concrete problem is two-fold: the bean is clearly request scoped instead of view scoped and you're doing business job inside a getter method instead of the (post)constructor or an (action)listener method.
A request scoped bean get recreated on every individual request and is not reused on subsequent requests on the same view. All properties like buttonsCount are reinitialized to their defaults. When clicking a command link/button in a datatable, you need to make sure that exactly the same datamodel is been preserved as in initial request. If you don't do that, JSF won't be able to find the action to be invoked. The bean needs to live as long as you're interacting with the same view by returning null or void. This can be achieved by putting the bean in the view scope by #ViewScoped (or when you're still on obsolete JSF 1.x, by Tomahawk's <t:saveState> component).
A backing bean getter method should only return data which is already prepared beforehand, not to prepare the data itself. The getter method is supposed to solely be an access point to the data. This is because the getter method can be called multiple times during a request-response cycle, especially if referenced in an UIData component and/or the rendered attribute.
See also:
commandButton/commandLink/ajax action/listener method not invoked or input value not updated
Why JSF calls getters multiple times

Related

Dynamically instantiate composite components on page, on clicking CommandButton

Following on from the response by the legendary BalusC to this post:
How to programmatically or dynamically create a composite component in JSF 2
Had I sufficient points I would attach a comment to that post -- but I don't have sufficient points .
Problem as follows.
I'm trying to set up a commandButton which adds a JSF composite component dynamically to an xhtml file. With the intention that clicking it multiple times will put multiple instances of the component on the page.
So I have a button on an XHTML file:
<h:commandButton action="#{assessingController.addQuestion}" value="Add a Question"></h:commandButton>
Which calls a method on AssessingController:
public void addQuestion() {
UIComponent parent = null;
includeCompositeComponent(parent, "http://xmlns.jcp.org/jsf/composite/components", "questionComposite", "someId");
}
UIComponent parent = null; -- because it has to be instantiated or referenced somehow, before being passed into includeCompositeComponent . But as noted below - making it null might be causing the null pointer exception, (so what should I do instead?)
includeCompositeComponent method is as per the JSF 2.2 method referred to by BalusC in the above post:
public void includeCompositeComponent(UIComponent parent, String taglibURI, String tagName, String id) {
FacesContext context = FacesContext.getCurrentInstance();
UIComponent composite = context.getApplication().getViewHandler()
.getViewDeclarationLanguage(context, context.getViewRoot().getViewId())
.createComponent(context, taglibURI, tagName, null);
composite.setId(id);
parent.getChildren().add(composite);
}
When I click on the commandButton, logs as follows:
javax.faces.el.EvaluationException: java.lang.NullPointerException
at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:101)
at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:102)
at javax.faces.component.UICommand.broadcast(UICommand.java:315) ...
Caused by: java.lang.NullPointerException
at controllers.AssessingController.includeCompositeComponent(AssessingController.java:123)
AssessingController.java:123 is this line:
parent.getChildren().add(composite);
Composite is not null (checked that).
So, obviously perhaps, - parent is null and that's where the problem is.
So how can I better reference UIComponent parent to begin with?
Do I need to make it refer to something on the xhtml file? I'm presuming it needs some kind of a placeholder which will serve as the parent(?). Right now all there is on the xhtml page is the commandButton.
Thank you all.
The parent is supposed to represent the component where you'd like to include the composite component in.
Imagine that you ultimately want to end up with this plain XHTML representation:
<h:panelGroup id="questions">
<your:questionComposite />
</h:panelGroup>
You should then supply exactly that <h:panelGroup> component as parent.
<h:form>
<h:commandButton ... action="#{bean.addQuestion}" />
</h:form>
<h:panelGroup id="questions" />
public void addQuestion() {
UIComponent parent = context.getViewRoot().findComponent("questions");
// ...
}
Or, by passing the concrete component itself:
<h:form>
<h:commandButton ... action="#{bean.addQuestion(questions)}" />
</h:form>
<h:panelGroup id="questions" binding="#{questions}" />
public void addQuestion(UIComponent parent) {
// ...
}
Unrelated to the concrete problem: there's a thinking/design mistake here. You should rather use an <ui:repeat><your:compositeComponent> and then feed from a #ViewScoped bean to the <ui:repeat> a dynamically sized list of entities representing the composite's model value.
<h:form>
<h:commandButton ... action="#{bean.addQuestion}" />
</h:form>
<ui:repeat value="#{bean.questions}" var="question">
<your:questionComposite value="#{question}" />
</ui:repeat>
private List<Question> questions;
public void addQuestion() {
questions.add(new Question());
}
Anytime you need to deal with raw UIComponent instances in a backing bean, take a pause and carefully research or ask if you're really doing things the right way. Perhaps it belongs in a backing component instead, or could just be done with pure XHTML.
See also:
How to dynamically add JSF components
How does the 'binding' attribute work in JSF? When and how should it be used?

Include dialog from another file via backing bean

I have a Primefaces project in which I am trying to replicate the behavior of a desktop application. Because of the nature of the desktop application, there are quite a few popup dialogs, which cause the processing of the page to become very slow (initial page load: 10-20 seconds, AJAX requests: 6-10 seconds).
I have separate files for all the dialogs already, and I want to use the backing bean to pop them up as dialogs without having to use <ui:include> in my main files. Is there a way to do this?
e.g.:
<p:commandButton id="showSearchDialog"
action="#{managedBean.showSearchDialog()}"/>
<p:dialog widgetVar="searchDialog">
</p:dialog>
public class ManagedBean {
public void showSearchDialog() {
//Some sort of function that knows to process the contents of searchDialog.xhtml
// and insert it into the relevant <p:dialog>
RequestContext.getCurrentInstance().execute("PF('searchDialog').show()");
}
}
If your goal is to reduce the size of the page, I'd approach it with conditional rendering of the dialog itself as determined by a backing bean property which would be set by the command action:
<p:commandButton id="showSearchDialog"
action="#{managedBean.showSearchDialog()}"
update="dialogs"
oncomplete="PF('searchDialog').show()" />
<h:panelGroup id="dialogs" layout="block">
<p:dialog widgetVar="searchDialog" rendered="#{managedBean.currentDialog eq 'search'}">
<ui:include src="searchDialog.xhtml" />
</p:dialog>
</h:panelGroup>
public class ManagedBean {
private String currentDialog;
public String getCurrentDialog() { return currentDialog; }
public void showSearchDialog() { currentDialog = "search"; }
}
Then you could conditionally render all of your dialogs within the 'dialogs' block and use ajax to refresh the rendered content dynamically.
Another option to consider is using the PrimeFaces Dialog Framework, which allows you to dynamically render an external page at runtime.

disabled="#{not recordsTransferSearch.isDisabled}" does not call isDisabled() method

I'm trying to disable a jsf component just by defining a action method and bind it to the components "disabled" attribute.
My JSF component snippet
<h:form id="bulk_sch_form1">
<a4j:commandButton id="alls" value="a. Search records form this company"
action="#{recordsTransferSearch.actionSearch}"
reRender="srtDlGrd, dlod_msg_grd, pending_student_table"
disabled="#{not recordsTransferSearch.isDisabled}">
</a4j:commandButton>
</h:form>
Backing bean action method
public boolean isDisabled() {
if (searchResults != null) {
return true;
}
return false;
}
The searchResults just evaluating after a successful search result has been returned. But as stated in the title it's not calling the action method isDisabled() at all, thus nothing happens.
The action method is only calling when I'm refreshing the page.
Thanks.
You should either use disabled as a field getter, like in disabled="#{not recordsTransferSearch.disabled}", or if your EL supports method calls, i.e. is of version 2.2+, you should add empty parentheses, (), at the end of the method call, like in disabled="#{not recordsTransferSearch.isDisabled()}".
Note that as it stands, and contrary to what you suggest in comments, disabled="#{bean.isDisabled}" will trigger Property 'isDisabled' not found error.
As per your comments you do not fully understand how disabled attribute works in JSF. It seems that you expect the button to become enabled/disabled on some javascript event and/or via some changes made by some action/actionlistener methods. This is not the case. The button is disabled/enabled only when EL expression of disabled attribute evaluates to true/false correspondingly. You may even test it: when you remove the disabled attribute of HTML input, effectively making it enabled in client side and will call that button, you'll see that no action method will be called, but instead its disabled attribute will be reevaluated on server and, as it'll evaluate to false, no method will be called.
To make it work as expected, you need to rerender the command button via AJAX call (by specifying its id in reRender attribute of another <a4j:commandButton> that changes the result of isDisabled() method so that it'll return false), or synchronously (enforcing the needed evaluation of disabled) so that the disabled condition will be evaluated to false.
Also, it would be good to go through a basic example to get a grasp of how it all works.
The view:
<h:form>
<h:commandButton id="disabled" value="Disabled command button"
action="#{bean.disabledSubmit}"
disabled="#{not bean.disabled}">
</h:commandButton>
<h:commandButton id="simple" value="Enable a disabled button"
action="#{bean.simpleSubmit}">
<f:ajax render="disabled"/>
</h:commandButton>
</h:form>
The bean:
#ManagedBean
#ViewScoped
public class Bean implements Serializable{
private boolean searchResults = false;
public boolean isDisabled() {
return searchResults;
}
public String disabledSubmit() {
return null;
}
public String simpleSubmit() {
searchResults = true;
return null;
}
}

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

JSF viewscope values not getting displayed in the view properly

I have a managed bean under ViewScope. It has an instance variable inside it.
MetaData object has a inputItem object List.
#ManagedBean
#ViewScoped
public class ConBean implements Serializable {
private MetaData metadata;
#PostConstruct
#SuppressWarnings("unchecked")
public void init() throws IOException {
this.metadata = new MetaData ();
}
public void proc(){
List<InputItem> inputs= new ArrayList<InputItem>();
inputs.add(***** code to populate the inputItem List);
//after populating, inputs added to the metadata
metadata.setInputs(inputs);
}
//getters & setters
}
in my JSF , input list is populated inside a UI repeat.
<div id="inputplaceholder">
<ui:repeat value="#{conBean.metaData.inputs}" var="content">
</ui:repeat>
</div>
the div inputplaceholder is periodically updated using a richfaces poll.
<a4j:poll id="poll" interval="12000" action="#{conBean.proc}"
execute="#form" render="inputplaceholder"/>
The problem that I have is even though inputItems are set to the metaData object correctly inside the proc() method, when the view is rendered/partially updated, it doesn't get highlighted in the UI. so partial update takes no effect. I tried moving
this.metadata = new MetaData ();
inside the proc method but had no luck.
any ideas and help is highly appreciated.
thanks ...
Did the partial render really take place? This is impossible. There is namely no JSF component with the ID inputplaceholder. You assigned it to a plain HTML <div> element. Replace it by a fullworthy JSF component:
<h:panelGroup layout="block" id="inputplaceholder">
Also, since you used a relative ID in the render attribute, it will only scan for components in the same parent naming container component. The <ui:repeat> is such one, however the component with the desired ID is placed outside it. You'd like to use an absolute ID instead. Assuming that it's inside a <h:form> with a fixed ID:
<h:form id="myform">
<h:panelGroup layout="block" id="inputplaceholder">
...
then you should be referencing it in the render attribute as follows
render=":myform:inputplaceholder"

Resources