JSF2 + IceFaces 2 - Retrieve UIComponent from ViewRoot - jsf-2

I've got hard time resolving the following. My problem is quite simple : I would like to highlight in red the forms fields that triggered validation errors. The error messages are placed correctly in the FacesContext using a context.addMessage(...) line.
I'd like my system to be generic. All form fields having a message attached are automatically highlighted.
I've found on this site a link to this excellent article :
http://www.jroller.com/mert/entry/how_to_find_a_uicomponent
With it, I did implement a PhaseListener for the RENDER_RESPONSE phase, which do the following :
#Override
public void beforePhase(PhaseEvent event) {
// get context
FacesContext context = event.getFacesContext();
// iterate on all the clientIds which have messages
Iterator<String> clientIdsWithMessages = context.getClientIdsWithMessages();
while (clientIdsWithMessages.hasNext()) {
// get the clientId for the field component
String clientIdWithMessage = clientIdsWithMessages.next();
// split on ":"
String[] splitted = clientIdWithMessage.split(":");
UIComponent component = findComponentInRoot(splitted[splitted.length - 1]);
if (component != null) {
Map<String, Object> attributes = component.getAttributes();
if (attributes.containsKey("style")) {
attributes.remove("style");
}
attributes.put("style", "background-color: #FFE1E1;");
}
}
}
This perform perfectly well for almost all my usage.
Now, where it becomes a bit tricky, is that some of my forms have such code :
<ice:dataTable id="revisionDocuments" value="#{agendaBean.agenda.revisionsDocuments}" var="revision">
<ice:column>
<ice:inputText value="#{revision.sequenceAdresse}" id="revisionSequenceAdresse" />
</ice:column>
....
The generated form has several lines (one for each object of the revisionsDocuments list), and each element has a unique identifier (clientId) which looks like :
contentForm:revisionDocuments:0:revisionSequenceAdresse
With 0 changed for 1, 2, ... for each iteration.
Consequently, the code provided to search the UIComponent from ViewRoot does not work properly. All forms fields have the same "id". What surprise me more is : they have the same "clientId" in FacesContext too :
contentForm:revisionDocuments:revisionSequenceAdresse
I cannot distinguish, while going through the tree, if I do see the right form field or any of the others.
Does anyone have a hint to solve this ? Or another suggestion to implement the highlight of my fields ? I have to admit, I dont really like my code, I consider dirty to manipulate the viewRoot like I'm doing, but I could not figure out a better solution to have a generic highlight of my fields.
I'm running IceFaces 2.0.2 with JSF-Impl 2.1.1-b04 on JBOss AS 7.0.2.Final.
Thank you in advance for the answers.
Best regards,
Patrick

You should apply this in the client side instead. You've got a collection of client IDs with messages. One of the ways is to pass this information to JavaScript and let it do the job. You can find an example of such a PhaseListener in this article: Set focus and highlight in JSF.
Since JSF 2.0 there is however another way without the need for a PhaseListener. There's a new implicit EL variable, #{component} which refers to the UIComponent instance of the current component. In case of UIInput components, there's an isValid() method. This allows you to do something like:
<h:inputText styleClass="#{component.valid ? '' : 'error'}" />
with this in a CSS file:
.error {
background: #ffe1e1;
}
(yes, you can also do this in a style attribute, but mingling style with markup is a poor practice)
To abstract this away (so that you don't need to repeat it in every input), you can just create a composite component for this, something like <my:input>.

For completeness, here is the solution I finally found to highlight the fields that do have error messages with IceFaces 2.0.2 :
The basic idea is strictly the same than proposed by BalusC on http://balusc.blogspot.com/2007/12/set-focus-in-jsf.html
The piece of code I had to change with IceFaces is the small Javascript call :
<script>
setHighlight('${highlight}');
</script>
I could not find any IceFaces component which is re-rendered at each JS call. I found that placing the script into a panelGroup works most of the time. However, there was a case that did not work :
submitting the form with errors do trigger the JS.
then, re-submitting the form with errors on the same field than previous validation do NOT trigger the JS.
then, re-submitting the form with any error field having no more errors do trigger JS.
then, re-submitting the form with any non-errored field having an error do trigger JS.
For some reason, IceFaces do not render the panelGroup that contains the JS when the set of fields having errors is the same between two calls.
I tried to use the Javascript API with code like Ice.onAsynchronousReceive(), using Prototype library to attach an event to the AJAX completion of the commandButton, but had not much success with it. Some of my tests could run (with errors but did the job) and I could observe similar behavior.
Here is the trick I finally used (ugly but working) :
<ice:panelGroup>
<script type="text/javascript">
var useless = '#{testBean.time}';
setHighlight('${highlight}');
</script>
</ice:panelGroup>
The getTime() function simply return the current timestamp. The value is then always different and trigger the JS execution at any AJAX request.
Sadly, IceFaces do not have the RichFaces useful "oncomplete" attribute, which I do regret highly for this case.
Ugly solution, but funny and working.

Related

How to disable JSF components programmatically

We have a JSF application with <rich:tab> which shows fields depending of some configuration stored in a database, so the components are not defined in the .xhtml page but have to be generated programmatically such in this example:
Components are generated in a panel:
<rich:tab id="someTab" header="#{msg['someHeader']}" immediate="true">
<rich:messages/>
<h:panelGrid id="generatedComponentsContainer"/>
</rich:tab>
Component generation example (simplified for simplicity):
FacesContext ctx = FacesContext.getCurrentInstance();
UIPanel panel = (UIPanel) ctx.getViewRoot().findComponent("someForm:generatedComponentsContainer");
text = (UIInput) ctx.getApplication().createComponent(ctx, "javax.faces.Input", "javax.faces.component.UIInput");
text.getAttributes().put("label", someLabel);
panel.getChildren().add(text);
Those components have to be shown disabled depending on some condition, so I used the following code to disable each of them if needed:
if (!showEnabled) { text.getAttributes().put("disabled", "true"); }
This method works for UIInput and HtmlInputTextarea but it is not working for UICalendar, throwing a IllegalArgumentException (argument type mismatch).
How can I disable the calendar?
I have been also wondering if this code just disables the component at the client side leaving it enabled at the server. This would probably be a security threat as somebody could enable a component via Javascript and submit the form to the server. I am not sure about this being possible, please advise if I am wrong.
After further research I noticed there are some classes that extend the ones we were using in our project. Those classes have a getter/setter for the disabled attribute which also disables the component in the server side. I tested this disabling the components programmatically and removing the disabled attribute while browsing the page to allow edition and submit. When submitting the form, the values are setted in the request but ignored at the server side. Bean values remain unaltered.
The classes we have used:
HtmlInputTextarea instead of UIInput
HtmlInputText instead of UIInput
We were already using UICalendar, which fits the purpose
A sample of code:
HtmlInputText text = (HtmlInputText) ctx.getApplication().createComponent(
ctx, HtmlInputText.COMPONENT_TYPE, "javax.faces.component.html.HtmlInputText");
if (!showEnabled) { text.setDisabled(true); }
When debugging the contents of the HtmlInputText you can see a ComponentStateHelper object (named stateHelper) which stores the disabled state of the component (among other data). Its superinterface is StateHolder:
public interface StateHolder
This interface is implemented by classes that need to save their state
between requests.
I understand that server-side state of the component is stored in this object, but I am not sure whether it is stored only here or in more points, or even if my interpretation of its purpose is correct. Feedback from an expert would be very useful.

How to change the standard href="#" attribute of h:commandLink?

I am maintaining a JSF2 Ajax application and we are heavily using h:commandLinks and f:ajax tags for all actions - always only rerendering what is needed.
This does of course break the expected behaviour for the user when performing a right click on the links and choosing "Open Link in New Tab" etc.
I understand that f:ajax forces the href atribute of the resulting a element to be # and does all the magic post request trickery in the onclick function - I now want to provide fallback support for the "Open Link..." action by putting some meaningful link in the href attribute of the resulting <a> tag.
This would not break the "normal" onclick behaviour as the generated javascript always finishes with return false; but would allow me to send my users to some page using a normal GET request in case they want to open the link in a new window.
Is there a build in way to do this? Or could somebody point me in the right direction on where in the JSF lifecycle I would have to jump in to do this maybe using a phase listener?
Simplest would be to extend com.sun.faces.renderkit.html_basic.CommandLinkRenderer and override the renderAsActive() method accordingly. Mojarra is open source, just copy the method and edit the line where it says writer.write("href", "#", "href"). Replace the "#" string accordingly to your insight.
public class MyCommandLinkRenderer extends CommandLinkRenderer {
#Override
protected void renderAsActive(FacesContext context, UIComponent command) throws IOException {
// ...
}
}
To get it to run, register it as follows in faces-config.xml:
<render-kit>
<renderer>
<component-family>javax.faces.Command</component-family>
<renderer-type>javax.faces.Link</renderer-type>
<renderer-class>com.example.MyCommandLinkRenderer</renderer-class>
</renderer>
</render-kit>
Note that this tight couples your renderer to Mojarra. To be JSF implementation independent, you'd need to create a whole new renderer instead of extending a Mojarra specific renderer class.
Unrelated to the concrete problem, consider reading When should I use h:outputLink instead of h:commandLink?

Dynamically added option in SelectOneMenu control on a JSF form submits as null

I have a standard JSF h:form which contains an h:SelectOneMenu control. As long as I am selecting an item from the list which is populated when the page is rendered it works perfectly. I don't think it is important, but to put it in context, the value from the select is used to build a query which returns a list of matching records.
I've implemented the JQuery autocomplete box on the control and it still works just fine as long as I'm selecting one of the original values.
The problem comes when I enter a value not in the select control when the page is rendered. Using JQuery, I've set it up so that when a value not on the list entered, the value is added to the select as a new option.
I can verify that the option is added to the underlying select control, and selected through the javascript. However when the setter is invoked in the backing bean immediately after that, the value passed in to the setter is null, and the function to run the query is never reached. The following error is returned in the AJAX response, but I have yet to be able to find a place where the value is validated. It isn't a required field either.
Validation Error: Value is not valid
Here is my front end code:
<h:selectOneMenu id="make" styleClass="combobox" value="#{listBean.make}"
effect="fade" label="#{listBean.makeLabel}" >
<f:selectItems value="#{listBean.makeList}" />
</h:selectOneMenu>
And the setter in the bean:
public void setMake(String make) {
this.make = make;
}
I'm guessing I just need to find a way to include the new option in the makeList List on the backing bean, but I'm not sure how to do that. Any help or suggestions would be appreciated.
Java EE 6, GlassFish 3.1, Eclipse 3.7 - problem observed on both FireFox and Chrome
You need to provide the dynamically added item through <f:selectItems>, not through JavaScript. If the item is not present in <f:selectItems>, then you will get exactly this validation error. This is done so as part of safeguard against tampered/hacked requests in an attempt to get illegal/unprovided values into the server side.
Easier is to use a JSF component library. PrimeFaces for example has a <p:autoComplete> for the exact purpose.

Dependable drop down list in JSP, Struts2

i am working on a project and using Struts2 for it with JSP. In one JSP page, i have two drop down list say dd1 and dd2. The dd2 is dependable on dd1. The values in dd2 should be populated based on the dd1 value. Now i have a java class which gives me all the options for the drop down lists from the database and i am displying them in my JSP using the SELECT tag. How to make the dd2 dependable on dd1? I dont have any knowledge of Ajax. Please help.
Not a very big fan of any Ajax plugin.If you do not want to use any big fancy Ajax tags and are ready to learn a bit of JQuery or any other java-script framework, i suggest you to use simple ajax call.
Play around with the events when the value in your parent drop-down change call a function and make an Ajax call to your action which can return the values in JSON format (use struts2 JSON plugin), parse the JSON data and fill the other drop-down.
benefits of this approach are
More flexible.
Much light and faster.
No need to add any UN-necessary dependencies. Other approach (IMO not the good one) use Struts2-Jquery plugin to get it done but get ready for any undesirable behavior or any other issue and you have to reply on the Plugin community.
Here is an example as suggested
<s:select list="states" label="State" name="state" onchange="ajaxCallForDistrict();"
id="stateList"></s:select>
<s:select list="districts" label="District" name="district" id="district" />
what i am doing here is that District are dependent upon state so once user select a State from first select i am calling a java-script function on onchange event and here is my ajaxCallForDistrict() function
function ajaxCallForDistrict()
{
var selectedState = document.getElementById("stateList");
var statedata = selectedState.options[selectedState.selectedIndex].value;
var formInput='state='+statedata;
$.getJSON('search/getDistricts',formInput,function(data) {
$('.result').html('' + data.districts + '');
$.each(data.districts,function(index, value){
var districtid = document.getElementById("district");
var option=new Option(value,value);
try{
districtid.add(option);
}
catch(e){
districtid.appendChild(option);
}
});
});
}
what i am doing in above function is that i am fetching the value of selected state and giving the Ajax call to getDistricts Action which is simply creating a list of district for the provided state and returning back the result as JSON data.
for filling the District select i am looping through the result (JSON) and appending the element in the select box
JSP are servlets inside so there no way how to make interaction after response is send to client
i would recommend struts2-jquery-plugin - you will be able to solve this problem using ajax and this framework is good place to start if you are not interested in learning javascript (jQuery), showcase
I am not sure but may be this can help you...
jsp page
<s:select name= "" lable = "" onchange="javaScriptFunction(element that you have selected);"></s:select>
JavaScript---
<script>
function javaScriptFunction(element)
{
document.LoginForm.action=element+".action";
document.LoginForm.submit();
}
</script>
Now map these action in struts.xml
My suggestion is use one action but several different methods.
After that populate the other dropdown Box by using s:iterator.

Telerik MVC Grid fails to bind after calling .ajaxRequest

I have something of a weird situation going on. I'm trying to build a Telerik MVC grid in a custom HTML helper which implements some other custom functionality. (Amongst other things, it renders a form to the right of the grid when a row is selected. We're not using the in-box editing features of the grid due to UI standardization. The whole requirement is that, for simple list-of-values tables in a database, we'd like a minimal-code approach. One line in the HTML, a few lines of Javascript at most, and boom, done.)
Everything works -- except rebinding the data dynamically. The grid renders, its selection works, the form displays, the form saves at blur events. The grid hits the OnDataBinding event, but nothing happens after that. It never gets to the OnDataBound event, and it never hits the internal bindTo nor bindData methods on the grid object itself.
"Enough" of the code (a lot of it can't be revealed) is thus (HTML helper):
public static void ListOfValuesEditorFor<TModel, TModelCollection>(this HtmlHelper<TModelCollection> htmlHelper, string gridName, string refreshAction, string refreshController, string loadItemUrl, IEnumerable<TModel> model) where TModel : class where TModelCollection : IEnumerable<TModel>
{
var factory = HtmlHelperExtension.Telerik<TModelCollection>(htmlHelper);
var grid = factory.Grid(model);
grid = grid.Name(gridName).Pageable(pager => pager.Enabled(false)).Selectable(select => select.Enabled(true)).Filterable(filter => filter.Enabled(false)).Scrollable().Sortable(sort => sort.Enabled(false));
grid = grid.DataBinding(binding => binding.Ajax().Select(refreshAction, refreshController));
grid = grid.ClientEvents(events =>
{
events.OnDataBound("Telerik.ListOfValues.OnDataBound");
events.OnDataBinding("Telerik.ListOfValues.OnDataBinding");
events.OnRowSelect("Telerik.ListOfValues.SelectRow");
});
var textControls = new List<string>();
int idColumn = -1;
grid = grid.Columns(columns =>
{
int cellCount = 0;
foreach (var prop in typeof(TModel).GetProperties())
{
// Populates columns, creates text entry controls in the list,
// handles some other proprietary work.
// SNIP
}
});
// Container for the form
var formDivBuilder = new TagBuilder("div");
// Build out the form
// SNIP
// Render to the response
var response = HttpContext.Current.Response;
response.Write("<input type=\"hidden\" id=\"loadItemUrl\" value=\"" + loadItemUrl + "\" />");
response.Write("<input type=\"hidden\" id=\"idColumnIndex\" value=\"" + idColumn.ToString() + "\" />");
grid.Render();
response.Write(formDivBuilder.ToString(TagRenderMode.Normal));
}
That HTML helper is called thusly:
<% using (Html.BeginForm()) {
Html.ListOfValuesEditorFor("JobTitleGrid", "RefreshJobTitles", "Home", "/Home/LoadJobTitle", Model);
} %>
On the Javascript side of the world, all OnDataBound and OnDataBinding do is display messages indicating that they've been hit. In fact, they won't even make it to the production version of the code; they're in there for debugging purposes now.
OnSelect displays and populates the form. This is happening correctly.
The form itself updates the object any time a text field's onChange event fires. This portion is validated as functional. This is done via a $.ajax() call, which again, is validated to function.
The success callback from that $.ajax() call is thus:
function onSubmitComplete(responseData, callbackData) {
// Some irrelevant junk here
$('#JobTitleGrid').data('tGrid').ajaxRequest();
}
The call to ajaxRequest functions. At the server, my grid action functions, returning an IList of the IJobTitle objects. At the client, OnDataBinding fires, displaying its message. OnDataBound never fires, and the grid display does not update.
I know this is somewhat outside the bounds of the way Telerik controls are normally used, but the sheer amount of code necessary to use them encourages my team to try to create reusable entities (such as these custom HTML helpers) wherever possible. For the simpler controls (text boxes, calendars, etc), our custom helpers have always "just worked." The grid, though, is presenting this problem.
Any ideas on why we never get to binding the data? More importantly, how to fix that?
After coming up with the solution, I'd briefly considered deleting the question -- Telerik's grid is only minimally involved here. However, I know first-hand how hard it is to troubleshoot code when you're building on top of frameworks, which are built on top of frameworks, which are further built on top of frameworks. :) So hopefully this answer will help the next guy down the line.
The actual issue turned out to be a serialization exception from the DAL call in the grid action. This seemed odd to me, since I used the exact same call to populate both the pre-loaded view in the Index action and the response from the GridAction, but sure enough, if I debugged down in the Javascript deeply enough, I eventually found it. The exception wasn't being handled (pro-tip: implement an OnError handler for the grid), and thus client-side rebinding failed, as it had no data to bind.
Once I resolved the serialization issue, everything just magically worked, and we were down to about 20 lines of code to implement an entire generic entity data entry screen.

Resources