I used to develop rich client interface applications using Spring MVC mixed with jQuery and html; However, because we don't have a good designer currently, I thought to go with JSF 2.2 using PrimeFaces 5.2 which should give me professional interface without a regular designer.
I have good knowledge about how to use JSF as a component based framework, but my concern is that I want to avoid unnecessary calls to the server as much as possible since I have tens of thousands of requests daily. So, I adhered to use jQuery with JSF and will avoid ajax calls as much as possible to update the view, unless I need to completely restructure it based on some user selections.
Here is the scenario I tried:
I have a table from which the user can select to hide/show columns based on checkboxes selection per each column. Now, as I said I don't want to use ajax to rerender the view for each select/unselect and I used jQuery instead.
After I made my first draft page and tried to play around as I used with jQuery, I found that I had nastily to navigate the page source using firebug and build complex jQuery to hide p:dataTable columns.
Is that normal? I mean, when I look to the code and see how much it depends on Primefaces html structure generation, I feel unconfident.
Am I using JSF unwisely or this is the truth of JSF?
You can use all the jQuery/JS you want over the JSF views. The main problem is that model doesn't keep updated, as long as you don't sync the state. To see that hiding/showing a column isn't that complex, here you've got a basic full-working example of it using jQuery and its style selector:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:p="http://primefaces.org/ui"
xmlns:ui="http://java.sun.com/jsf/facelets">
<h:head />
<h:body>
<script>
function switchColumn() {
if ($('.style1').is(":visible")) {
$('.style1').fadeOut(1000);
} else {
$('.style1').fadeIn(1000);
}
}
</script>
<h:form>
<p:commandButton type="button" onclick="switchColumn();"
value="Switch" />
<p:dataTable var="test" value="#{testBean.values}">
<p:column styleClass="style1">
#{test.id}
</p:column>
<p:column>
#{test.val1}
</p:column>
</p:dataTable>
</h:form>
</h:body>
</html>
#ManagedBean
#ViewScoped
public class TestBean {
private List<TestClass> values = new ArrayList<TestClass>();
public TestBean() {
values.addAll(Arrays.asList(new TestClass(1, "val1"), new TestClass(2,
"val2")));
}
public List<TestClass> getValues() {
return values;
}
public class TestClass {
private Integer id;
private String val1;
public TestClass(Integer id, String val1) {
this.id = id;
this.val1 = val1;
}
public Integer getId() {
return id;
}
public String getVal1() {
return val1;
}
}
}
It must be said JSF is designed to control the request contents and the model updates for you. Is not as light as SpringMVC in these terms, as you cannot choose what to send (not totally). So, if being worried about request/response performaces, maybe you should go with other framework.
See also:
Is there a way to add columns datatable without rerendering the entire table
Related
I have a table and in each row user can click on a link which triggers book availability check. So I have a commandLink with action and it works, but this action is executed every time user clicks on a link. I want it to be available only once. Also I don't want to hide link after click as it has onclick code which hides and shows details. Is it possible to remove action from commandlink after executing action?
The answer covered in Is it possible to use EL conditional operator in action attribute? is one of the ways that you can solve this. With that being said, since the release of JSF 2.2, there are also other alternatives. While removing the action attribute in JSF is problematic (it can be done with some trickery) - another solution is to use actionListeners together with an f:event binding that is connected to the preValidate event. This allows you to remove any of the connected actionListeners whenever you choose to do so.
Here is a complete solution with an event listener that modifies the component prior to it being processed for the view. Basically, you can do something like this;
<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<h:head>
<title>Dynamic action demo</title>
</h:head>
<h:body>
<h:form>
<h:dataTable var="row" value="#{removeActionBackingBean.rows}">
<h:column>#{row.primaryColumn}</h:column>
<h:column>#{row.hasBeenClicked}</h:column>
<h:column>
<h:commandButton actionListener="#{removeActionBackingBean.onPressed(row)}">
<f:attribute name="clicked" value="#{row.hasBeenClicked}"/>
<f:event listener="#{removeActionBackingBean.onModify}" type="preValidate" />
<f:ajax event="click" render="#form" />
</h:commandButton>
</h:column>
</h:dataTable>
</h:form>
</h:body>
</html>
For the backing bean, here is a solution with a complete model (using Lombok);
#Data
#Named
#ViewScoped
public class RemoveActionBackingBean implements Serializable {
private List<Row> rows;
#PostConstruct
private void init() {
rows = new ArrayList<>();
for (int i = 0; i < 10; i++) {
rows.add(new Row(RandomStringUtils.randomAscii(10)));
}
}
public void onPressed(Row row) {
row.hasBeenClicked = true;
System.out.println(String.format("'%s' has been pressed!", row.primaryColumn));
}
public void onModify(ComponentSystemEvent event) {
final boolean isRowClicked = (boolean) event.getComponent().getAttributes().get("clicked");
if (isRowClicked) {
for (ActionListener al : ((UICommand) event.getComponent()).getActionListeners()) {
((UICommand) event.getComponent()).removeActionListener(al);
}
}
}
#Data
#RequiredArgsConstructor
public class Row {
private #NonNull String primaryColumn;
private boolean hasBeenClicked;
}
}
The key sections to look at is f:event and the onModify() method binding. As you can see, we simply check if a certain "row" is considered as clicked - if this is the case, we clear all the actionListeners currently defined on the component. Effectively, there will be no actionEvent called when the button is pressed.
While the above solution modifies the actionListeners of a button, it can be adopted and used for other types of components and when you want to modify certain attributes of a component based on some condition - so it's extremely useful to know this trick.
This question already has an answer here:
Ajax update/render does not work on a component which has rendered attribute
(1 answer)
Closed 6 years ago.
I'm having a issue in PrimeFaces panel update.
I have one main panel which contains two output panel. Each output panel may contains one button which is swap panel. The swap panel button is used to swap the output panel from one to another.
If I update the button action for render the panels I need to provide the main panel Id it works fine. But for a tree structure hierarchy, If I mean to give the two output panel Ids It doesn't render the panel. The Button action called only once when I put the log to confirm that.
I will attach my code samples given below:
renderingPanel.XHTML
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.org/ui"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:c="http://java.sun.com/jsp/jstl/core">
<h:head>
<title>
Panel Rendered
</title>
</h:head>
<h:body>
<f:event listener="#{PanelRendered.initializePageAttributes}" type="preRenderComponent"/>
<h:form id="panelFormId">
<p:panel id="mainPanelId" widgetVar="mainPanelId">
<p:outputPanel id="mainOutputPanel" rendered="#{PanelRendered.mainPanelRendered}">
<h:outputText value="hello main"/>
<p:commandButton id="mainSwap" value="Swap To Sub Panel" update="mainOutputPanel,subOutputPanel" action="#{PanelRendered.mainButtonAction}" icon="ui-icon-transferthick-e-w"/>
</p:outputPanel>
<p:outputPanel id="subOutputPanel" rendered="#{PanelRendered.subPanelRendered}">
<h:outputText value="hello sub"/>
<p:commandButton id="subSwap" value="Swap To Main" update="subOutputPanel,mainOutputPanel" action="#{PanelRendered.subButtonAction}" icon="ui-icon-transferthick-e-w"/>
</p:outputPanel>
</p:panel>
</h:form>
</h:body>
</html>
PanelRendered.Java
public class PanelRendered
{
private boolean mainPanelRendered;
private boolean subPanelRendered;
private Logger logger = Logger.getLogger(PanelRendered.class);
public PanelRendered()
{
File configFile = new File("/home/nafeel/Applications/apache-tomcat-7.0.34/webapps/treetable/conf" + File.separator + "log4j.properties");
if (configFile.exists())
{
PropertyConfigurator.configure(configFile.getAbsolutePath());
}
else
{
System.out.println("Configuration Logger File not available");
}
}
public String mainButtonAction()
{
logger.info("Enter inside main button action");
mainPanelRendered = false;
subPanelRendered = true;
return null;
}
public String subButtonAction()
{
logger.info("Enter inside sub button action");
mainPanelRendered = true;
subPanelRendered = false;
return null;
}
public void initializePageAttributes()
{
logger.info("Enter inside initializepage");
mainPanelRendered = true;
subPanelRendered = false;
}
/**
* #return the mainPanelRendered
*/
public boolean isMainPanelRendered()
{
return mainPanelRendered;
}
/**
* #param mainPanelRendered the mainPanelRendered to set
*/
public void setMainPanelRendered(boolean mainPanelRendered)
{
this.mainPanelRendered = mainPanelRendered;
}
/**
* #return the subPanelRendered
*/
public boolean isSubPanelRendered()
{
return subPanelRendered;
}
/**
* #param subPanelRendered the subPanelRendered to set
*/
public void setSubPanelRendered(boolean subPanelRendered)
{
this.subPanelRendered = subPanelRendered;
}
}
Can you follow some guideline
JAVA code
Use proper naming for bean as your class name is PanelRendered and you are using the same name in xhtml file and you havent post your #ManagedBean #ViewScoped so i assume your bean name on xhtml should be panelRendered not PanelRendered.
Use #PostConstruct to initialize variable in JSF bean. avoid to use java constructor.
XHTML code:
I just change the bean name from PanelRendered to panelRendered and i am update panel like this update="mainPanelId"
And your code is working fine at my end, if you have any error please post here.
Your question
But for a tree structure hierarchy, If I mean to give the two output panel Ids It doesn't render the panel.
if the component with rendered="false" it is not generated any HTML code on browser, so you did not find the id="subOutputPanel" tag in your outputed HTML code, and when you click on command button it call the back bean method and come to update update="mainOutputPanel,subOutputPanel" it did not find the subOutputPanel id and ajax call will fail, and you will not get correct behavior or UI. and from next time it did not call any thing, for stop call back bean method you can study what happened after prime faces get ajax fail. hope this will help you.
I'm a bit of a n00b when it comes to JSF, but I have had similar issues. I think the Booleans in your PanelRendered class should have getters named as follows:
public boolean isMainPanelRendered()
public boolean isSubPanelRendered()
The expression language used in the XHTML file stays the same. It seems to be a convention that the expression language will add "is" to the front and capitalise the letter before searching the bean for the property(function or variable).
So this:
rendered="#{PanelRendered.mainPanelRendered}"
rendered="#{PanelRendered.subPanelRendered}"
stays the same.
Edited: added } at last of rendered.
Edited: realised it would need a getter for the private member variable
I am very new to JSF and this is the first project I work on (got a bit too lazy with jsp) so please forgive me if the question is trivial.
So I have a Super Class Device
#Entity
#Table(name = "Devices")
public class Device
{
protected bool Authorized
public bool isAuthorized()
{ return this.Authorized;}
public void setAuthorized(bool Authorized)
{ this.Authorized = Authorized;}
}
and a sub class SensorDevice that extends the Super Class Device
public class SensorDevice extends Device
{
// has its own properties which dont matter
}
and a Managed Bean UIDeviceManager
#ManagedBean(name = "DeviceManager")
#SessionScoped
public class UIDeviceManager
{
private List<SensorDevice> Pending;
// in constructor, Pending List gets populated with the devices requiring Authorization
}
and an xhtml page which contains a Table for the Pending Devices
<p:dataTable var="device" value="#{DeviceManager.pending}">
<p:column headerText="Device Authorization">
<h:form>
<p:inputSwitch
value="#{device.isAuthorized()}"
binding="#{AuthorizationInputSwitch}"
offLabel="UnAuthorized"
onLabel="Authorized">
<p:ajax
event="change"
listener="#{device.setAuthorized(AuthorizationInputSwitch.value)}" />
</p:inputSwitch>
</h:form>
</p:column>
Now unless the syntax in the xhtml is completely messed up (I tried my best there and would appreciate guidance), the function setAuthorized for that particular device instance should be called (even with the wrong input, but will sort that later by modifying the setter function), but that doesnt happen, the Ajax doesnt get called. Instead, the inputSwitch tries to update its "value property source" and attempts to look for a property isAuthorized() in the class SensorDevice which it fails to find.
Now I am aware that this could be easily solved by making the Boolean Authorized public in the super class but as you can see it is also a JPA entity that is persisted in a database to keep track of the devices, so the only option is to keep it protected.
So how do I update parameters of a Super class from a sub-class instance in a Managed Bean from a public function rather than direct access to the parameter itself (I thought JSF looked for the setters and getters but whatever)
Btw value="#{device.isAuthorized()}"works correctly but if I try the property directly it fails ( I guess its obvious at this point )
One last thing, if the approach/architecture is wrong, please advise on what is the correct layout to achieve this functionality. I am sure there is a standard way to integrate JSF and JPA without duplicating entities and wrappers
I think you must use field name in value attribute of InputSwitch component like this:
<p:inputSwitch
value="#{device.authorized}"
binding="#{AuthorizationInputSwitch}"
offLabel="UnAuthorized"
onLabel="Authorized">
Instead of:
<p:inputSwitch
value="#{device.isAuthorized()}"
binding="#{AuthorizationInputSwitch}"
offLabel="UnAuthorized"
onLabel="Authorized">
JSF will use isAuthorized and setAuthorized method (uses Java Beans standard convention to recognize getter and setter methods)
So i think you don't need the ajax part to call setter method.
To emphasize Mojtaba's answer, this is how you access properties in JSF:
Facelets page:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html">
<h:head>
<title>Facelet Title</title>
</h:head>
<h:body>
<h:outputText value="#{myBean.entity.someBool}"/>
</h:body>
</html>
Managed bean:
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
#ManagedBean
#SessionScoped
public class MyBean {
private SomeEntity entity = new SomeEntity();
public SomeEntity getEntity() {
return entity;
}
}
Entity classes:
public class SomeEntity extends SomeParent {
}
public class SomeParent {
private boolean someBool = true;
public boolean isSomeBool() {
return someBool;
}
public void setSomeBool(boolean someBool) {
this.someBool = someBool;
}
}
JSF doesn't know or care that your objects are JPA entities, and standard inheritance and access rules apply.
See also
JavaBean conventions
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.
I have two pages. Search page is the first page that takes user inputs. Second page shows the result set in datatable. Second page has 3 panel for resultset, update and create all in single page. Depending upon the which button being clicked, I am rendering panels true and false.
<h:panelGroup styleClass="panelGroup"
id="resultSet" rendered="#{bean.truefalse1}">
.
.
</h:panelGroup
<h:panelGroup styleClass="panelGroup"
id="updateForm" rendered="#{bean.truefalse2}">
.
.
</h:panelGroup
<h:panelGroup styleClass="panelGroup"
id="createForm" rendered="#{bean.truefalse3}">
.
.
</h:panelGroup>
From the search page I am setting these create and update panels to false and displaying only resultset.After the row from the result set is clicked I am showing
updateForm panel but keeping create panel to false.
But here the problem is, If there is validation error, then the property that was set from search page is being lost and all the panels are shown.
How do I get the value(boolean true or false) that was set from search page previously, since I am not navigating to different page.
I have getters and setters for boolean property in second class. I even tried keeping hidden fields(i.e the boolean property that was set from search page).
Shouldn't all the submitted values be recovered after validation error. Or just the ones we type in the form.
What is the best solution?
Any help is highly appreciated!!!
You indeed need to transfer the very same boolean properties to the next request. You can in theory use <h:inputHidden value="#{bean.boolean1}" /> for this, but unfortunately those will only be set during update model values phase, while you actually need it to be available during apply request values phase. Besides, they will also go lost when a validation error occurs.
There are three ways to fix this non-intuitive behaviour of h:inputHidden (I've ever filed a bug against it at the Mojarra issue list, but they didn't seem to do anything with it).
First is to use the binding on the h:inputHidden instead:
<h:inputHidden binding="#{bean.hidden1}" />
This however requires changes in the way you get/set the boolean values in the backing bean code. For example:
private HtmlInputHidden hidden1 = new HtmlInputHidden(); // +getter +setter.
public void setBoolean1(boolean boolean1) {
hidden1.setValue(boolean1);
}
public boolean getBoolean1() {
return (Boolean) hidden1.getValue();
}
Second is to use Tomahawk's t:saveState instead.
<t:saveState value="#{bean.boolean1}" />
The major advantage is that you don't need to change anything in the backing bean code. It will restore the value early before the apply request values phase. You only need to add extra libraries if not done yet, but as Tomahawk provides much more advantages than only the t:saveState, such as the in basic JSF implementation missing components/features t:inputFileUpload, t:dataList, t:dataTable preserveDataModel="true", t:selectOneRadio layout="spread" and so on, it is worth the effort.
The third way is to store them in a session scoped bean, but you actually don't want to do that for request scoped variables. It would only give "wtf?" experiences when the enduser has multiple tabs/windows open in the same session.
Edit: as per the comments, here's an SSCCE of the second way:
JSF page:
<%# taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
<%# taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<f:view>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
</head>
<body>
<h:form id="form">
<h:inputHidden binding="#{myBean.hidden}" converter="javax.faces.Boolean" />
<h:commandButton value="submit" action="#{myBean.submit}"/>
<h:outputText value="Current boolean value: #{myBean.hidden.value}" />
</h:form>
</body>
</html>
</f:view>
MyBean class:
package mypackage;
import javax.faces.component.html.HtmlInputHidden;
public class MyBean {
private HtmlInputHidden hidden = new HtmlInputHidden();
public void submit() {
if (hidden.getValue() == null) {
hidden.setValue(true); // Set to true on 1st submit.
} else {
hidden.setValue(!((Boolean) hidden.getValue())); // Toggle true/false.
}
}
public HtmlInputHidden getHidden() {
return hidden;
}
public void setHidden(HtmlInputHidden hidden) {
this.hidden = hidden;
}
}
The relevant part of faces-config.xml:
<managed-bean>
<managed-bean-name>myBean</managed-bean-name>
<managed-bean-class>mypackage.MyBean</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
Playground environment is JSF 1.2_13 on Tomcat 6.0.20.