Myfaces, facesContext.messageList always empty unless it's parent has a rendered="#{not empty facesContext.messageList}" - jsf

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>

Related

index of ui:repeat is empty [duplicate]

I have following code:
<ui:repeat var="class2" value="#{bean.list}" varStatus="status">
<h:form id="#{class2.name}">
<h:outputText value="#{class2.name}" />
</h:form>
</ui:repeat>
However, when I open the page, it errors as follows:
component identifier must not be a zero-length String
But it is properly printed in the <h:outputText>. How is this caused and how can I solve it?
You can use EL in the id attribute of a JSF component, but the EL variable has to be available during view build time, while the JSF component tree is to be built. However, the <ui:repeat> runs during view render time, while the HTML output is to be generated based on JSF component tree. The <ui:repeat var> is not available during view build time and #{class2.name} evaluates to null which totally explains the error you got. That it works in <h:outputText> is because it runs during view render time.
If you replace <ui:repeat> by <c:forEach>, which runs during view build time, then it'll work as you intented. The <c:forEach> will namely generate physically multiple <h:form> components in the JSF component tree which each generate individually their own HTML output (in contrary to <ui:repeat>, wherein the very same <h:form> component is been reused multiple times to generate HTML output).
<c:forEach var="class2" items="#{bean.list}" varStatus="status">
<h:form id="#{class2.name}">
<h:outputText value="#{class2.name}" />
</h:form>
</c:forEach>
However, I really wonder why you need to do that. There's usually no need to dynamically assign component IDs. JSF will already ensure the uniqueness of the ID. The below example,
<ui:repeat var="class2" value="#{bean.list}" varStatus="status">
<h:form id="form">
<h:outputText value="#{class2.name}" />
</h:form>
</ui:repeat>
will end up in multiple forms with each an unique ID, suffixed with iteration index of the <ui:repeat>. If you actually need to use #{class2.name} for some JavaScript/jQuery purposes (you did nowhere state the concrete functional requirement in the question for which you thought that this would be the right solution, so it's merely guessing), then just wrap it in a plain vanilla HTML element:
<ui:repeat var="class2" value="#{bean.list}" varStatus="status">
<div id="#{class2.name}">
<h:form id="form">
<h:outputText value="#{class2.name}" />
</h:form>
</div>
</ui:repeat>
Or set it as style class of a JSF component, which is also just selectable via a CSS selector:
<ui:repeat var="class2" value="#{bean.list}" varStatus="status">
<h:form id="form" styleClass="#{class2.name}">
<h:outputText value="#{class2.name}" />
</h:form>
</ui:repeat>
See also:
JSTL in JSF2 Facelets... makes sense?

JSF update component from different form, boths forms with prependid false

Hi i have this sceneario, i have 2 forms each of them with preprendId=false, the first one have a h:panelGroup that i want to update from the second h:form. The problem that i have
is that i dont know what String i have to put for update my panelGroup. I tried with this strings:
panelGroupComboId and :panelGroupComboId
But i always get: Cannot find component with expression ":panelGroupComboId" referenced from...
Because i need to use preprendId=false at least in the first form (where my panelGroup is) i cannot set preprendIf=true, but if i did it i could update my component with any problem using: :loginFormRegistroId:panelGroupComboId
But remember i NEED to use preprendId= false, when i use preprendId=false i can see using
firebug that my h:panelGroup is converted as a div with the id panelGroupComboId, then thats why i dont how do i have to call for update it.
Is preprendId=true the only way to this works?
FIRST FORM
<h:form id="loginFormRegistroId" prependId="false">
<h:panelGroup id="panelGroupComboId" layout="block">
<select id="comboCarreraId" name="comboCarreraId" class="form-control">
<ui:repeat value="#{miBean.list}" var="obj">
<option value="#{obj.id}">#{obj.name}</option>
</ui:repeat>
</select>
</h:panelGroup>
</h:form>
SECOND FORM
<h:form prependId="false">
<p:remoteCommand
name="cargarCarrerasRemoteCommand"
process="#this"
actionListener="#{miBean.myListener}"
update="panelGroupComboId">
</p:remoteCommand>
</h:form>
By the way i DONT want to update the entire first FORM, just my h:panelGroup
Remove prependId from the forms. Update the code as
<h:form>
<p:remoteCommand
name="cargarCarrerasRemoteCommand"
process="#this"
actionListener="#{miBean.myListener}"
update=":loginFormRegistroId:panelGroupComboId">
</p:remoteCommand>
</h:form>

Using jQuery Mobile popup with JSF

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?

Setting <f:param> value with <ui:repeat> var

<h:form>
<ui:repeat value="#{sampleManagedBean.food}" var="food">
<h:commandLink value="Name" action="#{sampleManagedBean.outcome}">
<f:param name="name" value="ssd" />
<f:param name="v" value="#{food.boy}" />
</h:commandLink>
<h2>#{food.boy}</h2>
</ui:repeat>
</h:form>
I can't get the the second <f:param> value which is set based on <ui:repeat var>. I can get only the first one which is hardcoded.
The ui:repeat is an UI component while f:param is a taghandler (like JSTL). Taghandlers run during view build time before UI components which run during view render time
(see here).
In our case it means that in the view build phase f:param knows nothing about #{food.boy}. c:forEach will be fine, but if we call some kind of ajax action to change the size of#{sampleManagedBean.food} and rerender the form, we'll not see any changes on page. Because partial rerendering (ajax) affects only UI component tree. c:forEach is somewhere between hardcoding and ui:repeat, we'll have to reload the page to see changes.
try this way,
<h:form>
<ui:repeat value="#{sampleManagedBean.food}" var="food">
<h:commandLink value="Name" action="#{sampleManagedBean.outcome}">
<f:setPropertyActionListener value="ssd" target="#{sampleManagedBean.name}" />
<f:setPropertyActionListener value="#{food.boy}" target="#{sampleManagedBean.v}" />
</h:commandLink>
</ui:repeat>
</h:form>

Why doesn't h:commandButton execute the action method when combined with c:chose?

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>

Resources