I am trying to update a primefaces component after the 'isAdmin' value changes.
I am using the following HTML page:
<h:form id="form">
<p:tabView id="tabs">
...
<p:tab title="Admin" rendered="#{userSession.isAdmin}">
...
</p:tab>
</p:tabView>
</h:form>
My UserSession class:
#ManagedBean(name="userSession")
#SessionScoped
public class UserSession {
.
.
.
public boolean isAdmin;
public UserSession() {
isAdmin = false;
}
public void addRole(String role) {
if (role.equals("ADMIN") {
this.isAdmin = true;
}
role.add(role)
}
}
The addRole() method gets called and adds the role "ADMIN". I know this isn't a good way of doing things, but I'm only using it for testing purposes at the moment.
Then, under the class where I handle login and the addRole() method gets called, I am trying to update the component using:
RequestContext.getCurrentInstance().update("form");
I have been trying to update the form as I believe this is always visible in the DOM?
My issue is, is that this is simply not working and nothing is being updated.
Any help is greatly appreciated.
I figured out that I was always actually changing my UserSession object instead of the actual session bean itself.
Therefore, I did the following and just pointed to my bean instead:
// Previously
// UserSession us = new UserSession();
UserSession us = (UserSession) FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get("userSession");
Related
I have a <h:selectManyCheckbox> that has a required-validation on. If I submit the form, I get a validation error when nothing is selected. So far, this ist expected. However, if I do an ajax update on the checkbox then, I get a ClassCastException. But only if empty values are treated as null.
So, I have the following setup. In the web.xml I set
<context-param>
<param-name>javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL</param-name>
<param-value>true</param-value>
</context-param>
Then I have an xhtml-page like this:
<h:form id="main">
<h:selectManyCheckbox id="value" value="#{testcb.selected}" required="true" requiredMessage="Select at least one entry">
<f:selectItems value="#{testcb.available}"/>
</h:selectManyCheckbox>
<div><h:message for="value" style="color:red;"/></div>
<h:outputLabel for="checkit" value="Enter some text: "/>
<h:inputText id="checkit" value="#{testcb.text}">
<f:ajax event="change" execute="#this" render=":main:value"/>
</h:inputText>
<div><h:commandButton type="submit" value="Submit" action="#{testcb.action}"/></div>
</h:form>
And this backing bean:
#Named("testcb")
#SessionScoped
public class TestCBBean implements Serializable {
private final Set<TestValue> available = EnumSet.allOf(TestValue.class);
private final Set<TestValue> selected = EnumSet.noneOf(TestValue.class);
private String text;
public void action() {}
public Set<TestValue> getAvailable() { return available; }
public void setAvailable(Set<TestValue> available) {
this.available.clear();
this.available.addAll(available);
}
public Set<TestValue> getSelected() { return selected; }
public void setSelected(Set<TestValue> selected) {
this.selected.clear();
this.selected.addAll(selected);
}
public String getText() { return text; }
public void setText(String text) { this.text = text; }
}
And this enum:
public enum TestValue { ONE, TWO, THREE }
I am running this in Wildfly 26.0.1-Final (JavaEE 8). But this also happens in older versions (like Wildfly 15). What I am doing:
enter some text and leave the box: an ajax update runs setting the value successfully in the model
I press submit: the validation error for the empty checkboxes pops up
I modify the text in the input and leave the box: the ajax update results in the following Exception:
java.lang.ClassCastException: class java.lang.String cannot be cast to class [Ljava.lang.Object; (java.lang.String and [Ljava.lang.Object; are in module java.base of loader 'bootstrap')
com.sun.jsf-impl#2.3.17.SP01//com.sun.faces.renderkit.html_basic.MenuRenderer.getSubmittedSelectedValues(MenuRenderer.java:508)
com.sun.jsf-impl#2.3.17.SP01//com.sun.faces.renderkit.html_basic.SelectManyCheckboxListRenderer.encodeEnd(SelectManyCheckboxListRenderer.java:89)
javax.faces.api#3.1.0.SP01//javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:600)
javax.faces.api#3.1.0.SP01//javax.faces.component.UIComponent.encodeAll(UIComponent.java:1655)
com.sun.jsf-impl#2.3.17.SP01//com.sun.faces.context.PartialViewContextImpl$PhaseAwareVisitCallback.visit(PartialViewContextImpl.java:628)
com.sun.jsf-impl#2.3.17.SP01//com.sun.faces.component.visit.PartialVisitContext.invokeVisitCallback(PartialVisitContext.java:159)
javax.faces.api#3.1.0.SP01//javax.faces.component.UIComponent.visitTree(UIComponent.java:1457)
javax.faces.api#3.1.0.SP01//javax.faces.component.UIComponent.visitTree(UIComponent.java:1469)
javax.faces.api#3.1.0.SP01//javax.faces.component.UIForm.visitTree(UIForm.java:355)
On the ajax update the checkboxes are not submitted. But they seem to contain an empty string as submitted value from the validation step before.
When setting the context parameter to false this works. But I want to keep it on true. Any ideas how I could work around this problem?
Reproduced. This is indeed a bug in Mojarra.
It boils down to that the following method in UIInput superclass ...
#Override
public Object getSubmittedValue() {
if (submittedValue == null && !isValid() && considerEmptyStringNull(FacesContext.getCurrentInstance())) {
return "";
} else {
return submittedValue;
}
}
... is not overridden in UISelectMany superclass in such way that it returns new String[0] instead of "". This was an oversight during implementing Faces issue 671.
I have fixed it in Mojarra issue 5081.
In the meanwhile, until you can upgrade to the Mojarra version containing the fix, you can temporarily work around it by copy pasting the entire source code file of UISelectMany into your project while maintaining the package and adding the following method to it:
#Override
public Object getSubmittedValue() {
Object submittedValue = super.getSubmittedValue();
return "".equals(submittedValue) ? new String[0] : submittedValue;
}
I have a jsf page and an input field. When the event of this input field being blurred, I would like the method checkIfUsernameAvailable() of my ManagedBean to be called. Then the image should be rendered. The problem is that that method is either never called or just not found, no matter what I do. So could someone tell me how I should do it ?
My code here doesn't work, obviously because I never call the method checkIfUsernameAvailable in my jsf. I'm sure the answer is simple but I tried so much and I just don't find it. So I removed my tried calls to this method in the code so it's without errors.
on the f: ajax i put
JSF:
<h:inputText id="username" required="true" value="#{subscribeUser.user.username}">
<f:ajax event="blur" render="usernameCheck"}"></f:ajax>
</h:inputText>
<h:panelGroup id="usernameCheck" >
<h:graphicImage value="resources/images/success_indicator.png" rendered="#{subscribeUser.isIndicatorVisible.usernameSuccess}">
</h:graphicImage>
<h:outputText id="usernameError" value="#{subscribeUser.isIndicatorVisible.usernameSuccess}"/>
</h:panelGroup>
So you see subscribeUser.isIndicatorVisible.usernameSuccess should always be false. I would like that when the input field is blurred that I'd call checkIfUserAvailable() so then it would be true and my image would then be rendered.
ManagedBean:
public class SubscribeUser {
private User user;
private Map<String, Boolean> isIndicatorVisible;
#EJB
Userpersistence up;
public SubscribeUser() {
this.user = new User();
this.isIndicatorVisible = new HashMap<>();
this.isIndicatorVisible.put("usernameSuccess", false);
//...
}
public void checkIfUsernameAvailable() {
this.isIndicatorVisible.replace("usernameSuccess", true);
}
//getters & setters
}
You need the listener attribute.
<f:ajax event="blur" listener="#{subscribeUser.checkIfUsernameAvailable()}" render="usernameCheck"}" />
See also:
Java EE 7 tutorial - Sending an ajax request
By the way, validation is usually done by a normal validator, not an action method. But I gather that you still need to learn about that part.
See also:
JSF 2.0 validation in actionListener or action method
I have problem using JSF 2.0. The command button doesn't call the bean, I have already read the balusc answer commandButton/commandLink/ajax action/listener method not invoked or input value not updated but I don't think I'm encountering those cases,
this is the code I'm using:
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.org/ui"
xmlns:c="http://java.sun.com/jstl/core">
....
<ui:repeat var="skill" value="#{skillsView.skills}">
<h:form>
<h:commandButton value="#{skill}"
action="#{skillsController.removeSkillFromPublication}" ajax="true">
<f:param name="theskill" value="#{skill}" />
</h:commandButton>
</h:form>
</ui:repeat>
</ui:composition>
UPDATE 1 :
this my controller
#Controller
#Scope(value = WebApplicationContext.SCOPE_REQUEST)
public class SkillsController extends BController {
private static final Logger logger = LoggerFactory
.getLogger(SkillsController.class);
public void removeSkillFromPublication() {
logger.info("Deleting : " + getParameter("theskill"));
publicationService.removeSkillFromPublication(publicationDetailView
.getPublicationFullView().getId(), getParameter("theskill"));
skillsView.setSkills(publicationService
.getSkillsFromPublication(publicationDetailView
.getPublicationFullView().getId()));
}
}
i don't get the the logger neither the task is executed , i already tested with actionlistner with controller with actionevent and this doesent work
UPDATE 2 :
It's working with changing the SCOPE of the skillsView To WebApplicationContext.SCOPE_SESSION , anybody know why ?!!
#Controller
#Scope(value = WebApplicationContext.SCOPE_SESSION)
public class SkillsView {
List<String> skills;
private String currentSkill;
public List<String> getSkills() {
return skills;
}
public void setSkills(List<String> skills) {
this.skills = skills;
}
public String getCurrentSkill() {
return currentSkill;
}
public void setCurrentSkill(String currentSkill) {
this.currentSkill = currentSkill;
}
The h:commandButton attribute action requires you to return a String
Taken from the jsf-toolbox:
action
The action attribute accepts a method-binding expression for a backing bean action method to invoke when this component is activated by the user. An action method must be a public method with no parameters that returns a String. The returned string represents the logical outcome of the action (eg. "success", "failure", etc. ) and is used by the JavaServer Faces MVC framework to determine which view to display next.
[bolding by me]
Instead use the actionListener property:
<h:commandButton actionListener="${skillsController.removeSkillFromPublication()}" ajax="true">
<f:param name="theskill" value="#{skill}" />
</h:commandButton>
Keep in mind you will have to change the method-signature to have an ActionEvent as parameter:
public void removeSkillFromPublication(ActionEvent e) {
//...
See the toolbox:
actionListener
The actionListener attribute accepts a method-binding expression for a backing bean action listener method that will be notified when this component is activated by the user. An action listener method must be a public method with an ActionEvent parameter and a void return type.
Alternatively you could have your removeSkillFromPublication() return a String. But that seems to be the wrong approach here.
I am retrieving the user's locale from the database. Whenever user is login then page should display on that perticular language. So for this i am using below code in the JSF
<f:metadata>
<f:event type="preRenderView" listener="#{language.preferredLocale}"/>
</f:metadata>
<ui:define name="content_pane">
<f:view locale="#{language.localeCode}"/>
</ui:define>
//language bean code is
public void preferredLocale(ComponentSystemEvent e){
// code to retrieve locale for particular user
}
But the above code is not working to set the locale on the page onload. is there anyother way i can set the locale on the page load?
This is indeed not the right way. It's too late then. You need to make sure that <f:view locale> is already set right.
There are several ways to achieve this anyway, depending on your current application design. The simplest one would probably be the following:
#ManagedBean
#RequestScoped
public void LoginBean {
#ManagedProperty("#{localeBean}")
private LocaleBean localeBean;
public void submit() {
User user = userService.find(username, password);
if (user != null) {
localeBean.setLanguage(user.getPreferences().getLanguage());
// ...
}
// ...
}
// ...
}
use this
<f:view beforePhase=#{bean.viewBeforePhase} >
Suppose I want to navigate in my application, and include different facelet pages dynamically. I have a commandLink like this:
<h:commandLink value="Link" action="#{navigation.goTo('someTest')}">
<f:ajax render=":content" />
</h:commandLink>
And this is where I include the facelet:
<h:form id="content">
<ui:include src="#{navigation.includePath}" />
</h:form>
The Navigation class:
public class Navigation {
private String viewName;
public void goTo(String viewName) {
this.viewName = viewName;
}
public String getIncludePath() {
return resolvePath(viewName);
}
}
I have seen similar examples, but this doesn't work of course. As ui:include is a taghandler, the include happens long before my navigation listener is invoked. The old facelet is included, instead of the new. So far I get it.
Now to the headache part: How can I dynamically include a facelet, based on an actionListener? I tried to include the facelet in a preRender event, and a phaseListener before RENDER_RESPONSE. Both work, but in the event listener I can't include a facelet which contains an other preRender event, and in the phaseListener I get duplicate Id's after some clicks in the included facelet. However, inspecting the component tree tells me, there are no duplicate components at all. Maybe these two ideas were not to good at all..
I need a solution, where the page with the ui:include, or the Java class which includes the facelet, doesn't have to know the pages, which will be included, nor the exact path. Did anybody solve this problem before? How can I do it?
I am using JSF 2.1 and Mojarra 2.1.15
All you need to reproduce the Problem is this bean:
#Named
public class Some implements Serializable {
private static final long serialVersionUID = 1L;
private final List<String> values = new ArrayList<String>();
public Some() {
values.add("test");
}
public void setInclude(String include) {
}
public List<String> getValues() {
return values;
}
}
This in your index file:
<h:head>
<h:outputScript library="javax.faces" name="jsf.js" />
</h:head>
<h:body>
<h:form id="topform">
<h:panelGroup id="container">
<my:include src="/test.xhtml" />
</h:panelGroup>
</h:form>
</h:body>
And this in text.xhtml
<ui:repeat value="#{some.values}" var="val">
<h:commandLink value="#{val}" action="#{some.setInclude(val)}">
<f:ajax render=":topform:container" />
</h:commandLink>
</ui:repeat>
That's enough to produce an error like this:
javax.faces.FacesException: Cannot add the same component twice: topform:j_id-549384541_7e08d92c
For OmniFaces, I've also ever experimented with this by creating an <o:include> as UIComponent instead of a TagHandler which does a FaceletContext#includeFacelet() in the encodeChildren() method. This way the right included facelet is remembered during restore view phase and the included component tree only changes during render response phase, which is exactly what we want to achieve this construct.
Here's a basic kickoff example:
#FacesComponent("com.example.Include")
public class Include extends UIComponentBase {
#Override
public String getFamily() {
return "com.example.Include";
}
#Override
public boolean getRendersChildren() {
return true;
}
#Override
public void encodeChildren(FacesContext context) throws IOException {
getChildren().clear();
((FaceletContext) context.getAttributes().get(FaceletContext.FACELET_CONTEXT_KEY)).includeFacelet(this, getSrc());
super.encodeChildren(context);
}
public String getSrc() {
return (String) getStateHelper().eval("src");
}
public void setSrc(String src) {
getStateHelper().put("src", src);
}
}
Which is registered in .taglib.xml as follows:
<tag>
<tag-name>include</tag-name>
<component>
<component-type>com.example.Include</component-type>
</component>
<attribute>
<name>src</name>
<required>true</required>
<type>java.lang.String</type>
</attribute>
</tag>
This works fine with the following view:
<h:outputScript name="fixViewState.js" />
<h:form>
<ui:repeat value="#{includeBean.includes}" var="include">
<h:commandButton value="Include #{include}" action="#{includeBean.setInclude(include)}">
<f:ajax render=":include" />
</h:commandButton>
</ui:repeat>
</h:form>
<h:panelGroup id="include">
<my:include src="#{includeBean.include}.xhtml" />
</h:panelGroup>
And the following backing bean:
#ManagedBean
#ViewScoped
public class IncludeBean implements Serializable {
private List<String> includes = Arrays.asList("include1", "include2", "include3");
private String include = includes.get(0);
private List<String> getIncludes() {
return includes;
}
public void setInclude(String include) {
return this.include = include;
}
public String getInclude() {
return include;
}
}
(this example expects include files include1.xhtml, include2.xhtml and include3.xhtml in the same base folder as the main file)
The fixViewState.js can be found in this answer: h:commandButton/h:commandLink does not work on first click, works only on second click. This script is mandatory in order to fix JSF issue 790 whereby the view state get lost when there are multiple ajax forms which update each other's parent.
Also note that this way each include file can have its own <h:form> when necessary, so you don't necessarily need to put it around the include.
This approach works fine in Mojarra, even with postback requests coming from forms inside the include, however it fails hard in MyFaces with the following exception during initial request already:
java.lang.NullPointerException
at org.apache.myfaces.view.facelets.impl.FaceletCompositionContextImpl.generateUniqueId(FaceletCompositionContextImpl.java:910)
at org.apache.myfaces.view.facelets.impl.DefaultFaceletContext.generateUniqueId(DefaultFaceletContext.java:321)
at org.apache.myfaces.view.facelets.compiler.UIInstructionHandler.apply(UIInstructionHandler.java:87)
at javax.faces.view.facelets.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:49)
at org.apache.myfaces.view.facelets.tag.ui.CompositionHandler.apply(CompositionHandler.java:158)
at org.apache.myfaces.view.facelets.compiler.NamespaceHandler.apply(NamespaceHandler.java:57)
at org.apache.myfaces.view.facelets.compiler.EncodingHandler.apply(EncodingHandler.java:48)
at org.apache.myfaces.view.facelets.impl.DefaultFacelet.include(DefaultFacelet.java:394)
at org.apache.myfaces.view.facelets.impl.DefaultFacelet.include(DefaultFacelet.java:448)
at org.apache.myfaces.view.facelets.impl.DefaultFacelet.include(DefaultFacelet.java:426)
at org.apache.myfaces.view.facelets.impl.DefaultFaceletContext.includeFacelet(DefaultFaceletContext.java:244)
at com.example.Include.encodeChildren(Include.java:54)
MyFaces basically releases the Facelet context during end of view build time, making it unavailable during view render time, resulting in NPEs because the internal state has several nulled-out properties. It's however possible to add individual components instead of a Facelet file during render time. I didn't really have had the time to investigate if this is my fault or MyFaces' fault. That's also why it didn't end up in OmniFaces yet.
If you're using Mojarra anyway, feel free to use it. I however strongly recommend to test it thoroughly with all possible use cases on the very same page. Mojarra has some state saving related quirks which might fail when using this construct.