The p:menuBar and it's p:menuItems are generated at runtime. They render correctly and function perfectly with the exception that when any choice from the menu is clicked, nothing happens.
My index.xhtml is quite simple. It's just a p:tabView that composites my dataentry.xhtml in via a ui:include. The components on dataentry.xhtml are where the trouble is happening.
//dataentry.xhtml
<?xml version="1.0" encoding="UTF-8"?>
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:p="http://primefaces.org/ui">
<h:form id="dataEntryForm">
<p:menubar id="menuBar" binding="#{dataEntryBean.menuBar}" />
<p:dashboard id="dashboard" binding="#{dataEntryBean.dashboard}"/>
</h:form>
</ui:composition>
Here's the method that generates the p:menuBar. Again, all of this renders on the page correctly, it just doesn't do anything when you click on something.
private #NotNull Menubar spawnMenuBar() {
final MenuModel MODEL = new DefaultMenuModel();
{
final DefaultSubMenu SM = new DefaultSubMenu("Create New...", "ui-icon-circle-plus");
Arrays.stream(Type.values()).forEach(CT -> {
final String CT_ALT = CT.toInitialCaps();
final DefaultMenuItem DMI = new DefaultMenuItem(CT_ALT + " Card");
DMI.setId("createNew" + CT_ALT + "Type");
DMI.setParam("TYPE", CT.name());
DMI.setTitle("Creates a new " + CT_ALT + " type panel in the dashboard.");
DMI.setCommand("#{dataEntryBean.addNewTypePanel(param.get('TYPE'))}");
SM.addElement(DMI);
});
MODEL.addElement(SM);
}
{
final DefaultSubMenu SM = new DefaultSubMenu("Global Actions...", "ui-icon-alert");
// Implementation removed for brevity's sake...
MODEL.addElement(SM);
}
final Menubar MENU = new Menubar();
MENU.setModel(MODEL);
return MENU;
}
...and, finally, here's the method that all of those p:menuItems in the first p:subMenu are supposed to invoke.
public void addNewTypePanel(final String TYPE) {
System.out.println("Method Call: addNewTypePanel(" + TYPE + ")");
// Implementation removed for brevity's sake...
}
That println() in addNewTypePanel() never appears.
Bizarrely (and, I suspect, unrelated), the println() on the first line of this bean's constructor appears twice (and then a 3rd time when the appropriate tab in the p:tabView is loaded).
Any ideas?
doHope(HopeSeverity.VERY_HARD);
Edit #1: Based on Kukeltje's suggestion, I re-tested this code by commenting out the p:tabView entirely and copying the h:form from dataentry.xhmtl (see code block below). Unfortunately, the issue (issues?) were the same. Still no response on click, still see the bean's constructor being invoked three times.
// index.xhtml (alt version, same behavior)
<f:view>
<h:body>
<h3>Some Text</h3>
<h:form id="dataEntryForm">
<p:menubar id="menuBar" binding="#{dataEntryBean.menuBar}" />
<p:dashboard id="dashboard" binding="#{dataEntryBean.dashboard}"/>
</h:form>
</h:body>
</f:view>
For clarification, the bean is #SessionScoped and #ManagedBean. I am testing all of this on Tomcat 8.0.33 with MyFaces 2.2.10.
Edit #2: Based on Kukeltje's request, stripped out some information that doesn't appear to be involved in the issue.
Related
Dear friendly strangers,
using PrimeFaces 7.0 on JSF 2.2 I'm generating html-Code in my Bean and inject it in my xhtml with <h:outputText value="#{myBean.myHtml}" escape="false"/>. This naturally doesn't work with <p:.../> components, as they themselves generate/render actual html. The way I alter the data from my Database to get the final html is too complicated for html functions though, so I still wanna do it in my Java-Beans instead of using lots of ui:repeat and hypercomplex custom styles - even though I know this is not how jsf/PrimeFaces is meant to be used. Now checking the actual rendered html e.g. of a p:commandLink it gives
<a id="myContainerID:myComponentID" href="#" class="ui-commandlink ui-widget" onclick="PrimeFaces.ab({s:"myContainerID:myComponentID",f:"myContainerID"});return false;">myComponentValue</a>
,which I can generate easily, but the response-action called when receiving the component's Ajax request (s:"myContainerID:myComponentID") will be missing, which seems to be saved somewhere in the moment the actual html is generated with <p:...>.
Is there a way to manually set that response-action, if so how/where?
EDIT: Since (quoting PrimeFaces.ab function)
//ajax shortcut
ab: function(cfg, ext) {
return PrimeFaces.ajax.AjaxRequest(cfg, ext);
}
The PrimeFaces.ajax.AjaxRequest can be asynchronous or synchronous. The AjaxRequest uses the AjaxUtils, which handles all
send, process, response, and update.
PrimeFaces.ajax.AjaxRequest = function(cfg, ext) {
cfg.ext = ext;
if(cfg.async) {
return PrimeFaces.ajax.AjaxUtils.send(cfg);
}
else {
return PrimeFaces.ajax.Queue.offer(cfg);
} }
I suppose the answer, if there is any, should lay somewhere in AjaxUtils, but couldn't find it yet.
Thanks helluvalot for any suggestion/help.
EDIT 2: I did eventually manage to transcribe it all to the xhtml with nested ui:repeats and lots of custom styles, I'm still curious though whether there's a way to do it with in-Bean-generated html.
ExampleCode
myBean:
#ManagedBean(name = "myBean")
#SessionScoped
public class myBean {
private String html1;
private String html2;
#PostConstruct
public void init() {
html1 = "<p:commandLink id=\"myComponentID\" value=\"myComponentValue\" "
+ "action=\"#{someBean.doSomething()}\"";
html2 = "<a id=\"myContainerID:myComponentID\" "
+ "href=\"#\" class=\"ui-commandlink ui-widget\" "
+ "onclick=\"PrimeFaces.ab({s:\"myContainerID:myComponentID\","
+ "f:\"myContainerID\"});"
+ "return false;\">1. myComponentValue</a>";
}
public String getHtml1() {
return html1;
}
public void setHtml1(String html1) {
this.html1 = html1;
}
public String getHtml2() {
return html2;
}
public void setHtml2(String html2) {
this.html2 = html2;
}
}
myIndex.xhtml:
<h: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"
>
<h:head>
</h:head>
<h:body>
<h:form id="myContainerID">
<h:outputText value="#{myBean.html1}" escape="false" />
<h:outputText value="#{myBean.html2}" escape="false" />
</h:form>
</h:body>
</h:html>
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
In the following form, we try to return a user's input to JSF's h:inputText or PrimeFaces' p:inputText.
We experience strange behavior when non-Latin characters (Japanese, Hebrew, etc. ) are entered:
On first request we get unrecognized character set, but on the second request - we get a correct result.
Input/Output Examples (first run only):
Japanese:
input = 日
output = æ¥
Hebrew:
input = א
output = ×
JSF:
<?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"
xmlns:p="http://primefaces.prime.com.tr/ui">
<body>
<h:form>
<h:outputLabel value="Name:"/>
<h:inputText value="#{newTestController.registeredCustomerFirstName}"/>
<h:commandButton value="Continue" action="#{newTestController.RegisteredNewCustomer(actionEvent)}"/>
</h:form>
</body>
</html>
Backing Bean:
#ManagedBean(name = "newTestController")
#SessionScoped
public class NewTestController {
private String registeredCustomerFirstName;
public String getRegisteredCustomerFirstName() {
return registeredCustomerFirstName;
}
public void setRegisteredCustomerFirstName(String registeredCustomerFirstName) {
this.registeredCustomerFirstName = registeredCustomerFirstName;
}
public void RegisteredNewCustomer(ActionEvent actionEvent) throws Exception {
}
}
As commented above - it is needed to define a default-charset for the application server.
For glassfish: add <parameter-encoding default-charset="UTF-8" /> to glassfish-web.xml.
For other application servers see BalusC's blog regarding this issue.
This is related to < http://java.net/jira/browse/GLASSFISH-18007 >. That fix was made to prevent a warning message when we unconditionally set the encoding to UTF-8, which would seem to be what we want, but in this case we felt it safer to not do it.
I've created a related issue in Mojarra, < http://java.net/jira/browse/JAVASERVERFACES-2217 >. Bottom line: setting the encoding explicitly in the app configuration is the right solution. The implementation is already doing the right thing.
Specifying charset in the config file might be not enough.
Try using p:commandButton instead of h:commandButton. The p:commandButton by default uses ajax, while the h:commandButton does non-ajax submit.
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.