Type-appropriate searching in dropdowns and search boxes in frontend - jsf

I have used custom converter classes with annotations like #FacesConverter(forClass = Date.class) to override the JSF default string representation of Date and Boolean objects in the frontend. I have two problems with this:
Date works as expected – all Date objects have a consistent representation in the frontend. However, Boolean works only if I change the annotation to #FacesConverter("mypackage.presentation.BooleanFormatConverter") and then explicitly invoke it in my xhtml, each time I want to use it, as <f:converter converterId="mypackage.presentation.BooleanFormatConverter" />.
I am changing the string representation of booleans to Yes/No, and I have filter boxes on boolean columns on my datatable with those string values. However, it does not seem to recognise that the string values correspond to the boolean true/false. I don't know how to fix this without explicitly changing the type of the booleans to string in the backend, then treating those as string columns in my datatable (and for other reasons, I'd rather keep types distinct and avoid explicitly converting everything to a string in the backend).

Quite simple in the end, following Kukeltje's suggestion: a boolean in the backing bean (which does not need to be done per-column) and changing the xhtml:
<p:selectCheckboxMenu value="#{myView.yesNoCheckbox}" label="Select" onchange="PF('tableE').filter()" scrollHeight="150" filter="true" filterMatchMode="contains">
<f:selectItem itemValue="#{true}" itemLabel="Yes"/>
<f:selectItem itemValue="#{false}" itemLabel="No" />
</p:selectCheckboxMenu>
the only disadvantage is that this part must be done manually per column (my datatable has an obscene number of columns, which is why I wanted a simple global way of doing this, such as a converter).

Related

Global date/boolean formatting in frontend of JSF application

I am developing a JSF dashboard web application which includes several very large PrimeFaces datatables. It would be really handy if I could set a global format for the representation of certain types – currently the only way I can think of is having backing bean methods like
public String formattedBoolean(Boolean inputBoolean) {
return inputBoolean ? "Yes" : "No";
}
which I could then manually call from the frontend (and similarly, I could use f:convertDateTime components for formatting date objects as string). However, this type of approach would require adding a lot of boilerplate XHTML, such as
<p:column headerText="Start Date" id="startDate"
sortBy="#{dataContainer.startDate}"
filterBy="#{dataContainer.startDate}">
<h:outputText value="#{dataContainer.startDate}">
<f:convertDateTime pattern="yyyy-MM-dd"/>
</h:outputText>
</p:column>
which I'd rather avoid. Does anyone know if there is a more elegant way of globally overriding the default Java string representation of objects in the frontend, or at least within the context of a data table?
Following advice above, I have decided to override the default FacesConverter for the given classes, as these are formats I'd want to present consistently in text throughout the application.

input component inside ui:repeat, how to save submitted values

I'm displaying a list of questions from database and for each question I have to display a list of options, in this case radio buttons.
<ui:repeat value="#{formData.questions}" var="question">
<div>
<p:outputLabel value="#{question.name}" />
<p:selectOneRadio value="#{formData.selectedOption}">
<f:selectItems value="#{formData.options}" />
</p:selectOneRadio>
</div>
</ui:repeat>
I need to save the checked option for each question.
How can I do this?
You need to associate the input value with the repeated variable var in some way. Right now you're not doing that anywhere and basically binding all input values to one and same bean property. So, when the form gets submitted, every iteration will override the bean property everytime with the value of the current iteration round until you end up getting the value of the last iteration round. This is definitely not right.
The simplest way would be to directly associate it with the object represented by var:
<p:selectOneRadio value="#{question.selectedOption}">
In your specific case, this only tight-couples the "question" model with the "answer" model. It's reasonable to keep them separated. A more proper solution in your specific case is to map it with currently iterated #{question} as key (provided that it has a proper equals() and hashCode() implementation, obviously):
<p:selectOneRadio value="#{formData.selectedOptions[question]}">
With:
private Map<Question, String> selectedOptions = new HashMap<>();
Regardless of the approach, in the action method, just iterate over it to collect them all.

Omnifaces validateMultiple component only takes UIInput values, any workaround for considering UIOutput too?

So, I have an Address to validate and it has 4 input fields and 4 output fields, basically the 4 output fields are city,state,county and municipality. There are not editable, so they will be populated by zipCode lookup only. But when I validate , I need to pass in all the values, the lookup values too.
<o:validateMultiple> only takes in Input Component values, so I tried to make them h:inputText too and then disabled=true since they aren't editable, but looks like <o:validateMultiple> ignores values of disabled input components as well. So, any alternatives?
Initially I did it bu embedding all the ids with respective bindings using f:attributes on the first inputText component and used JSF validator to grab getAttributes and validated that way, which worked OK, but since validateMultiple reduces lot of that, I wanted to use this , but looks like it is not straight forward.
Something like this could have helped :
<o:validateMultiple id="myId" components="foo bar baz" validator="#{bean.validateValues}" />
<h:message for="myId" />
<h:inputText id="foo" />
<h:inputText id="bar" />
<h:inputText id="baz" />
public boolean validateValues(FacesContext context, List<UIComponent> components, List<Object> values) {
// ...
}
Any help is appreciated!
Thanks!
Use <h:inputHidden> if you need hidden inputs.

How to implement foreach in jsf?

How do I create ofer_has_location objects (join object from location and ofer) using the current ofer and the selected items from the h:selectManyCheckBox
<h:selectOneMenu id="companyidCompany"
value="#{oferController.selected.companyidCompany}"
title="#{bundle.CreateOferTitle_companyidCompany}"
required="true"
requiredMessage="#{bundle.CreateOferRequiredMessage_companyidCompany}">
<f:ajax event="valueChange" execute="companyidCompany"
render="locationCollection" />
<f:selectItems value="#{companyController.itemsAvailableSelectOne}"/>
</h:selectOneMenu>
<h:outputLabel value="#{bundle.CreateOferLabel_locationCollection}"
for="locationCollection" />
<h:selectManyListbox id="locationCollection" value="locations"
title="#{bundle.CreateOferTitle_locationCollection}">
<c:forEach items="locations">
<f:selectItems var="locations"
value="#{oferController.selected.companyidCompany.locationCollection}" />
</c:forEach>
</h:selectManyListbox>
What you need to do in order to achieve 'connected elements' functionality:
Have two elements ( <h:selectOneMenu> and <h:selectManyLisBox> in your case), where the second one will be dependent on the selected option(s) of the first one. The second element must have an id in order to be rerendered afterwards.
Every HTML select element (that's rendered by both JSF tags of your choice) will have a set of options that are not supposed to be created via iterative element like <c:forEach> (though, it is in fact possible), but rather via the <f:selectItem>/<f:selectItems> tags (thus, remove your iterative tag in comment).
When values in the components are bound not as plain Strings, or primitive wrappers (Integer, etc.), but rather as model objects (YourClass objects, etc.), then you need to tell JSF two things: how can it print option's value from your class and how can it reconstruct an object from request parameter that is a string. For this you need to implement Converter, that is, explain JSF how to do the abovementioned transformations. Use this answer and BalusC's blog as reference points. Note the appropriate syntax for <f:selectItems itemValue="..."> here.
Model values bound by these two components also need to represent your classes, just in a same way as selected items' values. For <h:selectOneMenu> it is value="#{}"; for <h:selectManyListbox> it is value="#{}" with YourClass selectOneMenuValue and List<YourClass> selectManyListboxValues or YourClass[] selectManyListboxValues bean properties respectively.
Population of second select will be handled via <f:ajax> tag. As contents need to be calculated 'on the fly', the right spot to make it is within its listener attribute (i.e. to have List<YourClass> contentsOfSecondListbox = createListboxValues(YourClass oneMenuSelectedOption);) . As you'd desire to rerender the second element, specify its client id in render attribute of <f:ajax>. Example here.
In case you are binding, for example, to String/String[] values, you won't need the converter parts.
Try to go through it step by step to find out your errors and correct them.

Number converter wrapping ignores locale? bug?

Is it a Bug that the first does selects between a dot or comma (always dot) according to the used f:view locale? Or is there a rule how to nest number converters.
1.
<f:converter converterId="javax.faces.BigDecimal">
<f:convertNumber maxIntegerDigits="3" maxFractionDigits="2"/>
</f:converter>
2.
<f:convertNumber maxIntegerDigits="3" maxFractionDigits="2">
<f:converter converterId="javax.faces.BigDecimal"/>
</f:convertNumber>
You cannot nest converters. They will just be applied on the closest parent UIComponent in the order as they're declared. You can also not specify multiple converters. Only the last one would really be used. Each ValueHolder component can have only one converter while each EditableValueHolder can have more than one validators.
Just stick to <f:convertNumber>. When used on input components which are already bound to a property of BigDecimal type, it'll respect the type.

Resources