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.
Related
I'm experimenting with JSF scopes, and have some issues when using a session scoped bean within a request scoped bean.
The basic workflow is that the user would click a View button on an item from the home page, which then calls my code to load & display that item's information.
Here's my UPDATED code:
#ManagedBean(name="itemViewBean")
#RequestScoped
class ItemViewBean {
#ManagedProperty("#{itemBean}")
private ItemBean itemBean;
private ItemViewObject itemViewObject;
private int itemId;
public void init() {
itemViewObject = itemBean.readItem(itemId);
}
public String readItem(int itemId) {
// itemViewObject = itemBean.readItem(itemId); // moved to init();
return "/pages/viewItem.xhtml?faces-redirect=true";
}
public ItemViewObject getItemViewObject() {
return itemViewObject;
}
// getters & setters as needed
}
#ManagedBean(name="itemBean")
#SessionScoped
class ItemBean {
public ItemViewObject readItem(int itemId) {
// hardcoded creating a ItemViewObject for now.
// eventually would be loaded from the database.
....
}
}
My UPDATED view page then has things like:
<!-- added the metadata -->
<f:metadata>
<f:viewParam name="id" value="#{itemViewBean.itemId}" />
<f:event listener="#{itemViewBean.init}" type="preRenderView" />
</f:metadata>
<!-- same as before -->
<h:outputText value="#{itemViewBean.itemViewObject.description}" />
If my viewBean is request scoped (or view scoped), I get empty data on the view page. If the viewBean is session scoped, things work. And I'm not understanding why?
From what I see in my debugger, readItem(itemId) is called (from the home page when clicking a view button), but when the view page itself calls the getItemViewObject(), the itemViewObject is null.
What am I doing wrong?
UPDATE
I forgot to mention earlier how my home page was calling readItem method, and that was through a command button:
<h:commandButton class="btn btn-mini firefoxBtnMiniCorrection"
value="View"
action="#{itemViewBean.readItem(b.itemId)}"/>
Each item listed in the home page has its own View button.
Also forgot to mention that both the home page and my view page are using JSF Templates. I don't know if that matters.
From the comments below which people have made, I came up with the code changes above. And things work now. Using either request scope or view scope with ItemViewBean works now.
And I am surprised this worked! I'm not quite certain I fully understand why it works.
Are my changes the correct way to do things? Or is there a better way?
Also, I'm using JSF 2.1.
Update 2
It doesn't work. The scoping works, but I discovered that the itemId in the viewParam is always null. Why?
itemViewObject is private in a RequestScoped bean. After readItem() got a value for itemViewObject, this value will be forgotten after this request and will be null at the next request (`#RequestScoped').
I have the following command button in the view with ID "save":
<p:panel style="border:none;text-align:left;margin:0;">
<p:commandButton value="Save Document" id="save" icon="fa fa-save"
disabled="#{dIGRCController.digrc.qconce == '020'}">
<f:param name="validate" value="true" />
</p:commandButton>
<p:commandButton value="Clear" icon="fa fa-undo"></p:commandButton>
</p:panel>
I am trying to dynamically assign a different actionListener. If the user wants to INSERT some new record, I want it to call the insert method. If the user wants to update an existing record, it should call the update method.
Right now I am trying to do this:
#PostConstruct
public void init() {
// setting the action listener of the Save Document button
UIViewRoot viewRoot = FacesContext.getCurrentInstance().getViewRoot();
// UIComponent button = viewRoot.findComponent("save");
CommandButton button = (CommandButton) viewRoot.findComponent("save");
FacesContext context = FacesContext.getCurrentInstance();
MethodExpression methodExpression = context
.getApplication()
.getExpressionFactory()
.createMethodExpression(context.getELContext(),
"#{dIGRCController.updateDocument}", null,
new Class[] { DIGRCController.class });
button.addActionListener(new MethodExpressionActionListener(
methodExpression));
}
I am getting a null pointer exception on the line:
button.addActionListener(new MethodExpressionActionListener(
methodExpression));
What am I doing wrong? Is there another way to accomplish what I am trying to do? I am using JSF 2.2, PrimeFaces 5.3 and OmniFaces 1.11.
The findComponent() takes a client ID as argument not a component ID. The client ID is exactly the value of the generated HTML id attribute associated with the component in question. In case of a command button, usually the component ID of the parent <h:form> is prepended, separated by the naming container separator character which defaults to :.
Given this,
<h:form id="form">
<p:commandButton id="save" ... />
</h:form>
the client ID would be form:save.
CommandButton button = (CommandButton) viewRoot.findComponent("form:save");
See also this related question as to identifying and using client ID: How to find out client ID of component for ajax update/render? Cannot find component with expression "foo" referenced from "bar"
Unrelated to the concrete problem, manipulating the component tree in Java side is a poor practice. You'd better keep using XHTML+XML for this which is so much more self-documenting as to declaring/defining tree structures. You can use JSTL tags to dynamically build the view (note: this is different from dynamically rendering the view using rendered attribute!).
E.g.
<p:commandButton ... action="#{bean.save}">
<c:if test="#{bean.existing}">
<f:actionListener binding="#{bean.needsUpdate()}" />
</c:if>
</p:commandButton>
See also JSTL in JSF2 Facelets... makes sense?
Even more, you could just pass #{bean.existing} as method argument.
<p:commandButton ... action="#{bean.save(bean.existing)}" />
Both approaches are in turn admittedly kind of weird if #{bean.existing} refers the same bean as #{bean.save}. You could just check for that inside #{bean.save} itself.
public void save() {
if (existing) {
// UPDATE
} else {
// INSERT
}
}
Going further on that, this is IMO not the responsibility of frontend layer, but of the service layer. You pass the whole entity to the service layer which in turn checks based on PK if it's existing or not.
if (entity.getId() == null) {
// INSERT
} else {
// UPDATE
}
I have read a lot of posts at Stackoverflow but I didn't succeed in implementing the belowmentioned problem from my side.
the problem is: I need to type some text in <p:inputTextarea> and when clicking on button I need to get this value in the bean method.
I.e.:
<p:inputTextarea binding="#{input}"/>
<p:commandButton action="#{pessoaMB.adicionarContato(input.value)}" immediate="true"/>
with the bean method:
public void adicionarContato(String value) {
System.out.println(value);
}
The code I'm using gives me a null value.
I'm using #ViewScoped and cannot change this.
First of all, a side note: it is a bad practice to work with JSF components, you should work with model instead. I.e. don't use binding="#{input}", but stick to value="#{bean.text}".
Second, I doubt that immediate="true" is used appropriately in your setup. When used in a UICommand component like <h:commandButton> it will cause to skip JSF lifecycle for components with immediate="false" (or omitted, as it's the default), thus their value won't be set at all. Still, JSF will still preset submittedValue behind the scenes before the action method is executed.
Also, I strongly recommend to read BalusC's blog post Debug JSF lifecycle, as it is more than enlightening on the topic.
As to the solution, I'd suggest to deal with value binding with the bean, as presented in the first comment. With this approach you won't need action method parameter at all. Moreover, rethink your use of immediate attribute. If you think it's correct then you've got two choices: (1) use immediate="true" on <p:inputTextarea> or (2) switch to action="#{bean.action(input.submittedValue)}".
I would've done this :
<h:form>
<p:inputText value="#{pessoaMB.input}"/>
<p:commandButton value="add" action="#{pessoaMB.adicionarContato}" />
</h:form>
input would be here a pessoaMB property with a getter and setter (an IDE can autogenerate it).
private String input;
public String getInput() {
return input;
}
public void setInput(String input) {
this.input = input;
}
As for the adicionarContato method, it would be like this :
public void adicionarContato() {
System.out.println(input);
}
You should create a new class, i.e:
public class MyFields(){
String input1;
String input2; //and so on...
//getters and setters
}
Then, in pessoaMB create a property:
private MyFields inputFields; //getter and setter
Finally, in your xhtml file:
<h:form>
<p:inputText value="#{pessoaMB.inputFields.input1}"/>
<p:inputText value="#{pessoaMB.inputFields.input2}"/>
<!-- add more inputText components... -->
<p:commandButton value="add" action="#{pessoaMB.adicionarContato}" />
</h:form>
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
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