Consider the following JSF (& jQuery Mobile) markup:
<h:form>
Please login
<h:outputText id="greeting" value="Welcome, you have logged in" rendered="#{sessionScope.login == null ? false : true}"/>
<div id="login" data-role="popup">
<h:inputText value="#{requestScope.userName}"/>
<h:inputHidden value="#{requestScope.password}"/>
<h:commandButton value="login" action="#{loginBacking.processLogin}">
<f:ajax execute="#form" render="greeting"/>
</h:commandButton>
</div>
</h:form>
Obviously the above markup will not send the user input to the server, because jQuery Mobile popup needs <h:form/> tag inside popup. But if I put the <h:form/> tag inside popup, I cannot use <f:ajax render="greeting"/> (because the id "greeting" left outside).
How can I overcome this issue without using boilerplate Javascript codes?
You can just use absolute client IDs in <f:ajax render>. If you start with the JSF NamingContainer separator character, which defaults to :, then it will be resolved relative to the UIViewRoot. So you can just use render=":greeting".
You've by the way another problem. You cannot update components in JS/Ajax/client side which are by itself not rendered in JSF/server side, simply because there's then nothing in the HTML DOM tree which can be referenced and updated. You need to wrap the <h:outputText id="greeting"> in another component which is always rendered.
So, all with all, this should do for you:
Please login
<h:panelGroup id="greeting">
<h:outputText value="Welcome, you have logged in" rendered="#{login != null}"/>
</h:panelGroup>
<div id="login" data-role="popup">
<h:form>
<h:inputText value="#{requestScope.userName}"/>
<h:inputHidden value="#{requestScope.password}"/>
<h:commandButton value="login" action="#{loginBacking.processLogin}">
<f:ajax execute="#form" render=":greeting"/>
</h:commandButton>
</h:form>
</div>
(note that I have simplified the EL expression in the rendered attribute, it makes no sense to use a conditional operator to return boolean results while the expression by itself already returns a boolean result; I'd myself have preferred rendered="#{not empty login}" by the way)
See also:
How to find out client ID of component for ajax update/render? Cannot find component with expression "foo" referenced from "bar"
Why do I need to nest a component with rendered="#{some}" in another component when I want to ajax-update it?
Related
I'm trying to ajax-update a conditionally rendered component.
<h:form>
...
<h:commandButton value="Login" action="#{login.submit}">
<f:ajax execute="#form" render=":text" />
</h:commandButton>
</h:form>
<h:outputText id="text" value="You're logged in!" rendered="#{not empty user}" />
However, that does not work. I can assure that #{user} is actually available. How is this caused and how can I solve it?
It's not possible to re-render (update) a component by ajax if the component itself is not rendered in first place. The component must be always rendered before ajax can re-render it. Ajax is using JavaScript document.getElementById() to find the component which needs to be updated. But if JSF hasn't rendered the component in first place, then JavaScript can't find anything to update.
The solution is to simply reference a parent component which is always rendered.
<h:form>
...
<h:commandButton ...>
<f:ajax ... render=":text" />
</h:commandButton>
</h:form>
<h:panelGroup id="text">
<h:outputText ... rendered="#{not empty user}" />
</h:panelGroup>
See also:
Why do I need to nest a component with rendered="#{some}" in another component when I want to ajax-update it?
How to find out client ID of component for ajax update/render? Cannot find component with expression "foo" referenced from "bar"
I'm trying to ajax-update a conditionally rendered component.
<h:form>
...
<h:commandButton value="Login" action="#{login.submit}">
<f:ajax execute="#form" render=":text" />
</h:commandButton>
</h:form>
<h:outputText id="text" value="You're logged in!" rendered="#{not empty user}" />
However, that does not work. I can assure that #{user} is actually available. How is this caused and how can I solve it?
It's not possible to re-render (update) a component by ajax if the component itself is not rendered in first place. The component must be always rendered before ajax can re-render it. Ajax is using JavaScript document.getElementById() to find the component which needs to be updated. But if JSF hasn't rendered the component in first place, then JavaScript can't find anything to update.
The solution is to simply reference a parent component which is always rendered.
<h:form>
...
<h:commandButton ...>
<f:ajax ... render=":text" />
</h:commandButton>
</h:form>
<h:panelGroup id="text">
<h:outputText ... rendered="#{not empty user}" />
</h:panelGroup>
See also:
Why do I need to nest a component with rendered="#{some}" in another component when I want to ajax-update it?
How to find out client ID of component for ajax update/render? Cannot find component with expression "foo" referenced from "bar"
How to update a div and do partial submission using <h:commandButton>, I have previously used <p:commandButton> to do partial submission by setting the ajax attribute to true and the update attribute to :statusBlock, where the id of the <h:panelGroup> is statusBlock. I am having some designing issues with <p:commandButton> so I cannot use it so I have to use <h:commandButton>.
This is to be done by nesting a <f:ajax> in it.
In effects,
<p:commandButton ... process="#form" update=":statusBlock" />
does exactly the same as
<h:commandButton ...>
<f:ajax execute="#form" render=":statusBlock" />
</h:commandButton>
Note that the subtle difference with the PrimeFaces equivalent is that PrimeFaces defaults to #form in the process/execute, while the <f:ajax> one defaults to #this, so you might need to explicitly specify execute="#form" over all place where you didn't specify the process attribute in the PrimeFaces component.
See also:
Communication in JSF 2.0 - Ajax (asynchronous) POST form
You can just use the standard components alongside f:ajax e.g.
<h:form id="myForm">
<h:commandButton value="Push Me">
<f:ajax execute="myForm" render="statusBlock" />
</h:commandButton>
<h:panelGroup id="statusBlock">
</h:panelGroup>
</h:form>
I want to create a simple register form with JSF2 ajax and Bean Validation, which allows to display all error messages with custom format in the ui:repeat value="#{facesContext.messageList}" var="m" area when the validation fails. My code works well with Mojarra, but when I switch to Myfaces2.1, the messages never show with ui:repeat -- it only leaves an empty oltag there, and the facesContext.messageList seems to be empty. It still works with h:messages but that's not what I want.
However, today I found a wierd workaround: Adding the attribute rendered="#{not empty facesContext.messageList}" to one of the ui:repeat's parent component.
I can't understand why this rendered="..." matters because the errorDisplay component did get re-rendered everytime. Does this test #{not empty facesContext.messageList} change anything ?
<h:form>
Name:<h:inputText value="#{hello.name}" id="name"/>
<br/>
Age :<h:inputText value="#{hello.age}" id="age" converterMessage="'Age'必须是一个数字"/>
<h:commandLink value="register" type="submit">
<f:ajax execute="#form" render="errorDisplay"/>
</h:commandLink>
<h:panelGroup> <!--rendered="#{not empty facesContext.messageList}"-->
<h:panelGroup id="errorDisplay"><!--rendered="#{not empty facesContext.messageList}"-->
<span>发生以下错误:</span>
<ol style="color:red;">
<ui:repeat value="#{facesContext.messageList}" var="m">
<li>${m.detail}</li>
</ui:repeat>
</ol>
</h:panelGroup>
</h:panelGroup>
</h:form>
I've seen a strange problem in my project. It's that <h:commandButton/> does not execute the action method.
<c:choose>
<c:when test="#{empty param.t}">
// HTML
</c:when>
<c:when test="#{param.t eq 'normal'}">
// HTML
<h:form>
<h:commandButton value="ADD" action="#{addBean.doSomething}" />
</h:form>
<c:when>
</c:choose>
When I move <h:form> into first c:when, then the action method is called. Otherwise, it isn't. Why?
I'll ignore the syntax error in your EL (a missing }).
The command button won't be executed when #{param.t eq 'normal'} evaluates to false at the point the form submit request is to be processed. You need to maintain the same parameter for the subsequent request so that the button will be rendered so that JSF can confirm that the enduser is allowed to invoke the action. You can do this by adding a <f:param>:
<h:form>
<h:commandButton value="ADD" action="#{addBean.doSomething}">
<f:param name="t" value="#{param.t}" />
</h:commandButton>
</h:form>
Note that this is supported since JSF 2.0 only. On JSF 1.x you'd need to replace h:commandButton by a h:commandLink if you want f:param support.
Unrelated to the concrete problem, you should try to avoid JSTL as much as possible in your JSF views. If the intent is to render view parts conditionally (not to build view parts conditionally), then you should rather be using the JSF component's rendered attribute instead of a JSTL <c:choose> or <c:if>:
<h:panelGroup rendered="#{empty param.t}">
// HTML
</h:panelGroup>
<h:panelGroup rendered="#{param.t eq 'normal'}">
// HTML
<h:form>
<h:commandButton value="ADD" action="#{addBean.doSomething}" />
</h:form>
</h:panelGroup>