includeViewParams does not set values after validation - jsf-2

JSF 2.
A simple form with a single input that is validated. Using o:form in an attempt to fix the problem.
<f:view>
<f:metadata>
<f:viewParam name="info2" value="#{myClass.info2}" />
<f:viewParam name="info1" value="#{myClass.info1}" />
<f:event listener="#{myClass.init(}" type="preRenderView" />
</f:metadata>
<o:form includeViewParams="true">
<h:outputText value="Enter some info for #{myClass.info1}" />
<h:inputText id="input" value="#{myClass.info3}"
validator="myValidator" />
<br />
<h:commandButton action="#{myClass.action()}" value="Enter some info" />
<br />
<h:message for="input" />
<br />
<h:outputText value="#{myClass.info2}" />
</o:form>
</f:view>
Called with this URL
http://localhost:8080/test/test.xhtml?info1=hello&info2=there
When I put a value in the input field and the validation fails, the page comes back with
the same URL, but with both myClass.info1 and myClass.info2 set to null. It's great that
the URL is preserved, but it doesn't do much good if those parameters arent' being set in the
bean.
Why is that and how do I fix it?

Unfortunately, this is expected behavior. If validations phase fails, the update model values phase won't be executed. This not only applies to the input fields of the form, but also to the view parameters involved in the same request! Apparently, your bean is request scoped and then the view parameters set during the initial request will indeed be lost.
Your best bet is making the managed bean a #ViewScoped one instead and using <o:viewParam> instead to set the view parameter during the initial GET request only. You may only need to alter the init() method to skip the job when Faces#isPostback() returns true.

Related

JSF: f:viewParam doesn't call setter, f:viewAction doesn't call business-method [duplicate]

Can anyone clarify how we can use in general, or a in real world example, this snippet?
<f:metadata>
<f:viewParam id="id" value="#{bean.id}" />
<f:viewAction action="#{bean.init}" />
</f:metadata>
Process GET parameters
The <f:viewParam> manages the setting, conversion and validation of GET parameters. It's like the <h:inputText>, but then for GET parameters.
The following example
<f:metadata>
<f:viewParam name="id" value="#{bean.id}" />
</f:metadata>
does basically the following:
Get the request parameter value by name id.
Convert and validate it if necessary (you can use required, validator and converter attributes and nest a <f:converter> and <f:validator> in it like as with <h:inputText>)
If conversion and validation succeeds, then set it as a bean property represented by #{bean.id} value, or if the value attribute is absent, then set it as request attribtue on name id so that it's available by #{id} in the view.
So when you open the page as foo.xhtml?id=10 then the parameter value 10 get set in the bean this way, right before the view is rendered.
As to validation, the following example sets the param to required="true" and allows only values between 10 and 20. Any validation failure will result in a message being displayed.
<f:metadata>
<f:viewParam id="id" name="id" value="#{bean.id}" required="true">
<f:validateLongRange minimum="10" maximum="20" />
</f:viewParam>
</f:metadata>
<h:message for="id" />
Performing business action on GET parameters
You can use the <f:viewAction> for this.
<f:metadata>
<f:viewParam id="id" name="id" value="#{bean.id}" required="true">
<f:validateLongRange minimum="10" maximum="20" />
</f:viewParam>
<f:viewAction action="#{bean.onload}" />
</f:metadata>
<h:message for="id" />
with
public void onload() {
// ...
}
The <f:viewAction> is however new since JSF 2.2 (the <f:viewParam> already exists since JSF 2.0). If you can't upgrade, then your best bet is using <f:event> instead.
<f:event type="preRenderView" listener="#{bean.onload}" />
This is however invoked on every request. You need to explicitly check if the request isn't a postback:
public void onload() {
if (!FacesContext.getCurrentInstance().isPostback()) {
// ...
}
}
When you would like to skip "Conversion/Validation failed" cases as well, then do as follows:
public void onload() {
FacesContext facesContext = FacesContext.getCurrentInstance();
if (!facesContext.isPostback() && !facesContext.isValidationFailed()) {
// ...
}
}
Using <f:event> this way is in essence a workaround/hack, that's exactly why the <f:viewAction> was introduced in JSF 2.2.
Pass view parameters to next view
You can "pass-through" the view parameters in navigation links by setting includeViewParams attribute to true or by adding includeViewParams=true request parameter.
<h:link outcome="next" includeViewParams="true">
<!-- Or -->
<h:link outcome="next?includeViewParams=true">
which generates with the above <f:metadata> example basically the following link
<a href="next.xhtml?id=10">
with the original parameter value.
This approach only requires that next.xhtml has also a <f:viewParam> on the very same parameter, otherwise it won't be passed through.
Use GET forms in JSF
The <f:viewParam> can also be used in combination with "plain HTML" GET forms.
<f:metadata>
<f:viewParam id="query" name="query" value="#{bean.query}" />
<f:viewAction action="#{bean.search}" />
</f:metadata>
...
<form>
<label for="query">Query</label>
<input type="text" name="query" value="#{empty bean.query ? param.query : bean.query}" />
<input type="submit" value="Search" />
<h:message for="query" />
</form>
...
<h:dataTable value="#{bean.results}" var="result" rendered="#{not empty bean.results}">
...
</h:dataTable>
With basically this #RequestScoped bean:
private String query;
private List<Result> results;
public void search() {
results = service.search(query);
}
Note that the <h:message> is for the <f:viewParam>, not the plain HTML <input type="text">! Also note that the input value displays #{param.query} when #{bean.query} is empty, because the submitted value would otherwise not show up at all when there's a validation or conversion error. Please note that this construct is invalid for JSF input components (it is doing that "under the covers" already).
See also:
ViewParam vs #ManagedProperty(value = "#{param.id}")
Communication in JSF 2.0 - Processing GET request parameters
Send params from View to an other View, from Sender View to Receiver View use viewParam and includeViewParams=true
In Sender
Declare params to be sent. We can send String, Object,…
Sender.xhtml
<f:metadata>
<f:viewParam name="ID" value="#{senderMB._strID}" />
</f:metadata>
We’re going send param ID, it will be included with “includeViewParams=true” in return String of click button event
Click button fire senderMB.clickBtnDetail(dto) with dto from senderMB._arrData
Sender.xhtml
<p:dataTable rowIndexVar="index" id="dataTale"value="#{senderMB._arrData}" var="dto">
<p:commandButton action="#{senderMB.clickBtnDetail(dto)}" value="見る"
ajax="false"/>
</p:dataTable>
In senderMB.clickBtnDetail(dto) we assign _strID with argument we got from button event (dto), here this is Sender_DTO and assign to senderMB._strID
Sender_MB.java
public String clickBtnDetail(sender_DTO sender_dto) {
this._strID = sender_dto.getStrID();
return "Receiver?faces-redirect=true&includeViewParams=true";
}
The link when clicked will become http://localhost:8080/my_project/view/Receiver.xhtml?*ID=12345*
In Recever
Get viewParam
Receiver.xhtml
In Receiver we declare f:viewParam to get param from get request (receive), the name of param of receiver must be the same with sender (page)
Receiver.xhtml
<f:metadata><f:viewParam name="ID" value="#{receiver_MB._strID}"/></f:metadata>
It will get param ID from sender View and assign to receiver_MB._strID
Use viewParam
In Receiver, we want to use this param in sql query before the page render, so that we use preRenderView event. We are not going to use constructor because constructor will be invoked before viewParam is received
So that we add
Receiver.xhtml
<f:event listener="#{receiver_MB.preRenderView}" type="preRenderView" />
into f:metadata tag
Receiver.xhtml
<f:metadata>
<f:viewParam name="ID" value="#{receiver_MB._strID}" />
<f:event listener="#{receiver_MB.preRenderView}"
type="preRenderView" />
</f:metadata>
Now we want to use this param in our read database method, it is available to use
Receiver_MB.java
public void preRenderView(ComponentSystemEvent event) throws Exception {
if (FacesContext.getCurrentInstance().isPostback()) {
return;
}
readFromDatabase();
}
private void readFromDatabase() {
//use _strID to read and set property
}

How to use generated ids in EL (jsf, composite)?

I want to have a composite that composes
<h:form id="f_imgA" >
<h:graphicImage id="imgA"
onclick="document.getElementById('#{k_imgA.clientId}').value=mods(event)"
value="images/img?r=#{Math.random()}">
<f:ajax event="click" listener="#{mBean.handleEvent}"
execute="#this k_imgA" render="#this"></f:ajax>
</h:graphicImage>
<h:inputHidden id="k_imgA" binding="#{k_imgA}" value="#{mBean.keyX}" />
</h:form>
when I write
<comps:cimg imgId="imgA" />
The original purpose of this code is to send the modifier-states (Ctrl, Shift, Alt) to the server.
I have
<composite:interface>
<composite:attribute name="imgId" />
</composite:interface>
<composite:implementation>
<h:form id="f_#{cc.attrs.imgId}">
<h:graphicImage id="#{cc.attrs.imgId}"
onclick="document.getElementById('#{k_${cc.attrs.imgId}.clientId}').value=mods(event)"
value="images/img?r=#{Math.random()}">
<f:ajax event="click" execute="#this k_#{cc.attrs.imgId}"
listener="#{mBean.handleEvent}" render="#this">
</f:ajax>
</h:graphicImage>
<h:inputHidden id="k_#{cc.attrs.imgId}"
binding="k_#{cc.attrs.imgId}" value="#{mBean.keyX}" />
</h:form>
</composite:implementation>
which, quite expected, does not work. The offending expression is
#{k_${cc.attrs.imgId}.clientId}
which is intended to return the clientId of the hiddenInput with id k_imgA . As far as I know, EL cannot handle nested expressions like the one above, but it was worth a try. So is there a simple, straightforward way to get the clientId of k_imgA? I don't want to use more javascript, if this can be avoided.
Edit:
don't get confused about #{Math.random()}, it works just because I have a bean called "Math".
The javascript function mods is given by
<h:outputScript target="body">function mods(event) {return ((event.ctrlKey)?1:0)+((event.shiftKey)?2:0)+((event.altKey)?4:0)} </h:outputScript>
You don't need to fiddle with all those _#{cc.attrs.imgId}. Just give the composite a fixed id. They are already naming containers themselves. JSF will then worry about the rest.
<composite:implementation>
<h:form id="f">
<h:graphicImage id="i" ... />
<h:inputHidden id="k" ... />
</h:form>
</composite:implementation>
Usage:
<comps:cimg id="imgA" />
Generated HTML:
<form id="imgA:f" ...>
<img id="imgA:f:i" ... />
<input id="imgA:f:k" ... />
</form>
As to the JS attempt, you'd best use element's own ID as base:
<h:graphicImage onclick="document.getElementById(id.substring(0,id.lastIndexOf(':')+1)+'k').value='...'" />
Or, easier, if the hidden element is guaranteed to be the direct sibling of the image element in the generated HTML DOM tree, then just grab it by Node#nextSibling:
<h:graphicImage onclick="nextSibling.value='...'" />
<h:inputHidden ... />
The binding will never work as in your attempted construct, and even then you'd best do it via a Map in request scope or a so-called backing component.
See also:
Accessing JSF nested composite component elements in JavaScript
Integrate JavaScript in JSF composite component, the clean way
Rerendering composite component by ajax

Why does required="true" not fail validation when disabled="true" is specified

<a4j:outputPanel id="tapalSectionSendToPanel" ajaxsingle="true">
<h:inputText id="sendToId1" value="#{MainBean.SectionBean.sendTo}"
class="createresizedTextbox"
required="true" requiredMessage="#{msg.labl_required}"
disabled="true" />
<h:message for="sendToId1" style="color:red" />
</a4j:outputPanel>
i need to validate textbox for empty validation and should show required when i click button without entering any value in textbox. It works fine without disabled="true". Whats the alternative for my requirement.
First, required and disabled don't go well together, because they are mutually exclusive as per the JSF Spec:
required: Flag indicating that the user is required to provide a submitted value for this input component.
disabled: Flag indicating that this element must never receive focus or be included in a subsequent submit.
Like I said in the comments, you can just display a message when the user tries to submit the form without selecting a node:
<h:inputText id="sendToId1" value="#{MainBean.SectionBean.sendTo}"
styleClass="createresizedTextbox" required="true" readonly="true" />
<h:message for="sendToId1" value="#{msg.labl_required}"
rendered="#{facesContext.postback and facesContext.validationFailed}" />
As an alternative you can just display a text anywhere in your markup:
<h:outputText value="#{msg.labl_required}"
rendered="#{empty MainBean.SectionBean.sendTo}" />
disabled="true" disables the input (so it's skipped when the form is submitted), if you don't want the user to type in it use readonly="readonly"

actionListener not called inside <c:if>

I have the following code, Using jsf2.2, primefaces 3.2.
My requirement is to update the Project depending on the updateFlag.
when i use c:if (xmlns:c="http://java.sun.com/jsp/jstl/core") like the following code the action Listener for the Update commandButton is not called. but if i use < p:panel rendered="#{projectBean.updateFlag}" > instead of < c:if > it works. Please help i dint get it, i think i should use c:if but its not working.
<p:dialog widgetVar="projectUpdate" id="projectUpdatePanel" modal="false" >
<p:panel>
<c:if test="#{projectBean.updateFlag == false}">
<h:outputText value="Project Title" />
<p:inputText disabled="true" value="#{projectBean.selectedProjectDo.projectTitle}" />
<p:commandButton value="Update" disabled="true" />
<p:commandButton value="Cancel" actionListener="#{projectBean.cancelUpdate}" />
</c:if>
<c:if test="#{projectBean.updateFlag == true}">
<h:outputText value="Project Title"/>
<p:inputText value="#{projectBean.selectedProjectDo.projectTitle}" />
<p:commandButton value="Update" actionListener="#{projectBean.updateProject}" />
<p:commandButton value="Cancel" actionListener="#{projectBean.cancelUpdate}" />
</c:if>
</p:panel>
</p:dialog>
You better just use it the following way (put a condition on the disabled attribute)
<p:panel>
<h:outputText value="Project Title"/>
<p:inputText disabled="#{not projectBean.updateFlag}"
value="#{projectBean.selectedProjectDo.projectTitle}" />
<p:commandButton disabled="#{not projectBean.updateFlag}" value="Update"
actionListener="#{projectBean.updateProject}" />
<p:commandButton value="Cancel" actionListener="#{projectBean.cancelUpdate}" />
</p:panel>
In general : don't use JSTL tags unless you really need them...
This is a classic example of non-DRY code, which is bad. Daniel shows perfectly how to make it DRY, however he didn't explain the cause of your problem.
Based on the problem symptoms, this will happen when #{projectBean} is a view scoped bean. View scoped beans are stored in the JSF view state. So, view scoped beans are only available after restore view phase. However, JSTL tags runs during restore view phase, while the view scoped beans are not available yet. This causes creation of a brand new view scoped bean instance, which is then later replaced by the real view scoped bean which was stored in the restored JSF view state. The brand new and separate view scoped bean which is used by JSTL will have all its properties set to default and thus the block which has updateFlag=false will always be invoked.
See also:
JSTL in JSF2 Facelets... makes sense?

preRenderView-Event triggered before an action (commandbutton)

I am in a little bit of trouble with my preRenderView-Event. My page also uses a commandbutton to submit data but unfortunately the preRenderView-Event is always called before the buttons action is invoked.
The preRenderView-Event is responsible for loading data from a service. Since the page is simple and doesnt have to store any data I dont need any Session or ViewScoped-Beans.
As far as I understood the event is called in the Render-Response-Phase and the action in the button should happen somewhat earlier.
<h:body>
<f:metadata>
<f:viewParam name="id" value="#{bean.id}" converter="converter.SetOfLongConverter"/>
<f:event type="preRenderView" listener="#{bean.initializeData}" />
</f:metadata>
<ui:composition template="/META-INF/templates/myTemplate.xhtml">
<ui:param name="title" value="#{bean.title}"/>
<ui:define name="content">
<h:outputText value="#{bean.description}" />
<h:form>
<h:panelGrid columns="2">
<h:outputLabel value="Model" for="model" />
<h:inputText id="model" value="#{bean.model}" />
<h:outputLabel value="Manufacturer" for="manufacturer" />
<h:inputText id="manufacturer"
value="#{bean.manufacturer}" />
<h:outputLabel value="Year" for="year" />
<h:inputText id="year" value="#{bean.year}" />
</h:panelGrid>
<h:commandButton value="Create" type="submit" action="#{bean.create}" rendered="#{bean.createMode}"/>
<h:commandButton value="Save" type="submit" action="#{bean.save}" rendered="#{!bean.createMode}" />
</h:form>
</ui:define>
</ui:composition>
</h:body>
I added some Console-Messages to verify when the method in the bean is called and when I have the preRenderView-event it never happens.
Any advice what I am doing wrong here?
The <f:metadata> has to go inside <ui:define> of an <ui:composition>.
See also <f:metadata> tag documentation (emphasis mine):
Declare the metadata facet for this view. This must be a child of the <f:view>. This tag must reside within the top level XHTML file for the given viewId, or in a template client, but not in a template. The implementation must insure that the direct child of the facet is a UIPanel, even if there is only one child of the facet. The implementation must set the id of the UIPanel to be the value of the UIViewRoot.METADATA_FACET_NAME symbolic constant.
The simple reason is because the metadata is supposed to be view-specific, not template-specific.
Update: it turns out that the action method is not invoked at all. You've there a rendered attribute on the command button which seems according the symptoms to be dependent on a request based variable instead of a view based variable. Putting the managed bean in the view scope should fix this problem. See also commandButton/commandLink/ajax action/listener method not invoked or input value not updated

Resources