jsf2 f:selectItem itemLabel complex i18n render - jsf

I've a f:selectitem with its itemLabel and i want to render the label so "pretty"!
The problem:
<p:selectOneRadio id="selectRadio" value="#{somebean.somevalue}" layout="pageDirection" >
<f:selectItem itemLabel="#{msg['message.1']}" itemValue="1" />
<f:selectItem itemLabel="#{msg['message.2']}" itemValue="2" />
</p:selectOneRadio>
My properties i18n file:
message.1=some message by {0}
message.2=another message by {0}
I want to do a inline replacement of {0} by #{somebean.theUser}
The result should be something like this (bold included):
some message by <b>HUSTON</b>
In tag file i should do something like this (or in similar way)
<f:selectItem itemLabel="#{msg['message.1']{'<b>'+somebean.theUser+'</b>'}}" itemValue="1" />
In other words, i want to add the i18n string param replacement directly into itemLabel tag.
I've try with <f:facet name="itemLabel"> but nothing.
Someone can help me?
Thanks in advice,
Agharta

JSF won't let you do that from the markup side. There is no way to use for example <h:outputFormat /> for it.
Instead you need to simply create a bean, that will read the contents from properties file and format accordingly:
<p:selectOneRadio id="selectRadio" value="#{somebean.somevalue}" layout="pageDirection" >
<f:selectItem itemLabel="#{helperbean.someMessage}" itemValue="1" />
<f:selectItem itemLabel="#{helperbean.anotherMessage}" itemValue="2" />
</p:selectOneRadio>
That was JSF part, in your bean:
public String getSomeMessage() {
// Actually you need some common access helper, it is just simplified example
String messagePattern = null;
try {
ResourceBundle rb = ResourceBundle.getBundle("path/to/properties/file");
String messagePattern = rb.getString("message.1");
return MessageFormat.format(messagePattern, somebean.getTheUser());
} catch (MissingResourceException mre) {
logger.warn("Missing resource file or resource key", mre);
return "!message.1!"; // That will show you where the problem is
}
}
Few additional points regarding Localizability:
Please use meaningful key names. Something like "message.1" gives no context to translators. I don't know what you are up to, but maybe "some-module.select.message.radio.message.sent.by.pattern" will be something better - the translator needs to know a) where it will be displayed, b) what is the purpose of this text (whether it is some description, message pattern, general text, window/dialog title, button caption, etc.). Be sure to provide them such context.
Be sure to include formatting tags into the message itself. That means, the message should look like message.1=some message by <b>{0}</b> in your properties file. You'd be surprised how often these kind of tags need to be removed (or replaced with other emphasis means). You should be especially careful, since you seem to be doing something with RTL languages and bolded fonts does not work with them very well.

Related

p:selectCheckboxMenu Need Label for selecting all

Am tring to add label for check box [That is for checking all Checkboxs] p:selectCheckboxMenu as in the figure.
Here is the Code:
<p:selectCheckboxMenu value="#{dashBoardController.selectedColumns}"
styleClass="ui-selectcheckboxmenu-header"
label="Custom" filterMatchMode="startsWith"
panelStyle="width:220px">
<f:selectItem itemLabel="Status" itemValue="status"/>
<f:selectItem itemLabel="group name" itemValue="groupName"/>
</p:selectCheckboxMenu>
CSS:
.ui-selectcheckboxmenu-header:before
{
content: "All";
}
any suggestions ??
After some 5 min of CSS fights, i discover some maneer to do it (i think its not the normal way to do, but i did not find other way).
You can use the content style property to atach content to an html element from css. The p:selectCheckboxMenu header class is .ui-selectcheckboxmenu-header, so, if you try this:
// on your css file
.ui-selectcheckboxmenu-header:before
{
content: "MY TEXT FOR SELECTING ALL ON MANY CHECKBOX MENU";
}
you will have the expected result. More info about content property, visit this link.
Note: I recommed to give an css id on the specific p:selectCheckboxMenu to have the pretended title. If you wonna for all p:selectCheckboxMenu you can use the code as it's.
Hope it helps ^^

JSF Access viewParams from within a component

I have a component to display a entry. This component can appear multiple times within the same page, and therefore just takes an entity, passes it to the component's backing bean where all the magic happens.
The component's appearence can be affected by various parameters.
Now, it is a requirement, that no matter if I have 1 or 10 of those components, the "same" view-state should be recoverable using a deeplink.
Let's say my component looks like this:
<composite:interface componentType="itemView" >
<composite:attribute name="item" required="true" shortDescription="The item to display."/>
<composite:attribute name="urlPrefix" required="true" />
</composite:interface>
<composite:implementation>
<f:event type="preRenderComponent" listener="#{cc.init}" />
....
</composite:implementation>
it has a corresponding backing Bean, I can access. (leaving this out, as its not required)
Now, these components are embedded on a page, that looks like this:
<f:metadata>
<f:viewParam name="itemType"
value="#{listController.itemType}"></f:viewParam>
</f:metadata>
<ui:define name="content">
<!-- multiple occurences, ui repeat over list of items -->
<my:itemView item="#{currentItem}" urlPrefix="#{listController.nextLetter}">
As already mentioned, the state of every item can be affect, and the changed state musst be accessible by a deeplink. So, my idea was, to use <h:link> inside the component, prefix every attribute and use includeViewParams="true", so that every (not defaulted) state is part of the url.
<composite:interface componentType="itemView" >
<composite:attribute name="item" required="true" shortDescription="The item to display."/>
<composite:attribute name="urlPrefix" required="true" />
</composite:interface>
<composite:implementation>
<f:event type="preRenderComponent" listener="#{cc.init}" />
<h:link includeViewParams="true">
<f:param name="#{cc.attrs.urlPrefix}size" value="#{cc.largerSize}" />
larger
</h:link>
....
</composite:implementation>
Just as for the "larger" link, there are upto 10 links, providing different options.
The problem: include-view-params does include all the view-params from within the same component, and its parent (itemType), but NOT from other components. So, basically changing the style of "one" component, will drop the parameters of another component.
Expected:
The url already looks like ?itemType=something&asize=5&abackground=green
Clicking the "larger" link on the b component will give me : ?itemType=something&asize=5&abackground=green&bsize=5
Whats happening:
After clicking the link, the url looks like: ?itemType=something&bsize=5
so the attributes for "a" are dropped.
I noticed, that if i extend the <f:metadata> object on the listing-page with something like <f:viewParam name="asize" /> the aSize parameter will be preserved, when clicking something from within "b" - but of course if i need to list all parameters like THAT, i dont need components, because i can not predict the number of items.
So, i tried to add this just right inside the component (like the f:event): <f:viewParam name="#{cc.urlPrefix}size" /> - but that did not work.
Any Ideas on that (or alternative solutions? Note: Saving the view States in the database, and simple using a "single" id to reference that state is basically bad, because that would cause a big amount of datapolution. The View-State needs to stay transient, but recoverable :0) )
Finally i found a solution. It's not the best, but it works at least ;)
I created a method on the component's bean, that will return a query string, containing all values ecxcept the one(s) passed as a paremeter.
So, whenever I want to change the parameter asize=5, i call this function with param size and append the new parameter (Function will take care of excluding this parameter only, when the prefi matches, so no other components style would be affected.):
public String buildUrlWithout(String keyString) {
List<String> keys = ConversionHelper.Explode(keyString, ",");
// get all parameters from the url.
String prefix = this.getAttributes().get("urlPrefix").toString();
ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
Map<String, String> params = externalContext.getRequestParameterMap();
List<KeyValuePair<String, String>> result = new LinkedList<KeyValuePair<String, String>>();
outerfor: for (Map.Entry<String, String> kvp : params.entrySet()) {
for (String s : keys) {
if (kvp.getKey().equals(prefix + s)) {
continue outerfor;
}
}
// keep param
result.add(new KeyValuePair<String, String>(kvp.getKey(), kvp.getValue()));
}
String qStr = KeyValuePair.toQueryString(result);
return qStr;
}
KeyValuePair ist just a Helper-Class that behaves like a Map<S,T> but offers several functions like join(), split() etc.. (in this case toQueryString() is used)
Finally, from within a component, I use this like this:
<h:outputLink
value="#{cc.buildUrlWithout('size')}&#{cc.attrs.urlPrefix}size={cc.size +1}">
larger
</h:outputLink>
<h:outputLink
value="#{cc.buildUrlWithout('size')}&#{cc.attrs.urlPrefix}size={cc.size -1}">
smaller
</h:outputLink>
<h:outputLink
value="#{cc.buildUrlWithout('background,border')}&#{cc.attrs.urlPrefix}background=red&#{cc.attrs.urlPrefix}border=green">
red background + green border
</h:outputLink>

Using JSF Validator

I am using Prime faces 3.4.1 as component framework and JSF 2.0 as the server side framework
Following is my requirement
1) I have a field with label as "Meeting Required". Then I have SelectOneRadio with two options "No" and "Yes" with default value as "No". I am using JSF/HTML component h:SelectOneRadio.
2) I have another field which is Calendar component and this is a primefaces calendar component. When the user selects "Yes" which indicates the "Meeting is required" and the user should select a date from the calendar control.
3) If the user selects "Yes" and does not select a date, then a Validation message should be displayed indicating that the date should be selected.
I created a Custom Validation component and attached to the SelectOneRadio and I am able to see the selected value in the Custom validator. Now, I try to get the value from the Calendar component to check if the value is empty, through UIComponent.getParent().findCompoent("rvmDate"), I get the component but I do not know how to check if the date component is empty or contain any values.
Please help me out to get the date value selected by the user.
Please help me out to resolve the issue. Or is there any other way? Please find the source code.
XHTML Page
<h:selectOneRadio id="rvmMeetingOption"
readonly="#{wipMB.rvmMeetingOptionReadOnly}"
value="#{wipMB.requirementsMeeting}"
disabled="#{wipMB.rvmMeetingOptionDisabled}"
validator="#{wipMB.validateRVMDate}"
validatorMessage="Please enter RVM Date>
<f:selectItem itemLabel="No" itemValue="0"></f:selectItem>
<f:selectItem itemLabel="Yes" itemValue="1" ></f:selectItem>
<f:attribute value="#{rvmDateComp}" name="rvmDateComp"></f:attribute>
</h:selectOneRadio>
<p:calendar id="rvmDate"
readonly="#{wipMB.rvmMeetingDateReadOnly}"
disabled="#{wipMB.rvmMeetingDateDisabled}"
readonlyInput="true"
navigator="true" mode="popup"
pattern="dd/MM/yyyy"
value="#{wipMB.rvmDate}"
effect="explode"
yearRange="1900:2500"
style="margin-left:5px"
binding="#{rvmDateComp}"
</p:calendar>
<p:message id="rvmDateMsg" for="rvmDate" display="both" ></p:message>
Custom Validator
public void validateRVMDate(FacesContext context, UIComponent component, Object value)
throws ValidatorException
{
String invalidDate;
String rvmOption;
Date rvmDate;
String rvmDt = "";
try
{
FacesContext fc = FacesContext.getCurrentInstance();
rvmOption = value.toString();
DateFormat formatter = new SimpleDateFormat("E MMM dd HH:mm:ss Z yyyy");
UIInput rvmCompDt = (UIInput)component.getAttributes().get("rvmDateComp");
rvmDateId = rvmCompDt.getId();
rvmDt = rvmCompDt.getSubmittedValue() == null ? "" : rvmCompDt.getSubmittedValue().toString();
if (rvmOption.equals("1") && rvmDt.isEmpty())
{
FacesMessage msg = new FacesMessage("RVM date is required");
msg.setSeverity(FacesMessage.SEVERITY_ERROR);
fc.addMessage("rvmDateMsg", msg);
throw new ValidatorException(msg);
}
}
catch (Exception ex)
{
String msg = ex.getMessage();
}
}
In JSF each component is a little MVC stack of its own; there is the Model (stored as value), the Controller (the component object) and View (renderer). Validators and controllers are part of the architecture and are needed to move the values between the model and the view.
While JSF Validators play an important role, it is important only INSIDE this little MVC stack. They were not designed to "validate forms", they are made strictly to "validate component value". Lamentably, the name "validator" makes everyone who comes to JSF think, that each time any validating needs to be done, validator is the solution. Strangely, converters are not so abused.
In your case, building a custom validator created a strange situation, where:
validator and view have a cyclic dependency on each other,
there is a need to use hacks (such as "immediate") and low-level APIs,
hard-coding view logic in an unlikely place,
requires much more knowledge and is flaky. For example the logic of acquiring value from calendar might be different depending on whether radio button is before or after calendar in document order.
The problems above could be solved, but since they all arise from abusing JSF architecture, I think it would be better to rethink the problem. Since your validation concerns flow of application, it is a perfect fit for the action method, where all the complications will dissolve into a single, simple "if" statement with a conditional "addMessage".
You first need to remove immediate="true" from the <p:calendar>, otherwise it's not processed at all when the radio button is processed.
Then, to check if a string is null or empty, just do
String dt = (String) uiCalendar.getSubmittedValue();
if (dt == null || dt.isEmpty()) {
// dt is null or empty. Throw validator exception depending on the
// current radio button value. Note: you should not catch it yourself!
}
Note that this has nothing to do with JSF. It's just basic Java. Your initial attempt as if (dt == "") is indeed completely invalid. The String is an object, not a primitive. The == compares objects by reference, not by their internal value. Technically, you should have used if (dt.equals("")) instead, but the isEmpty() is nicer.
Unrelated to the concrete problem, a much easier way is to just check the radio button value in the required attribute of the calendar component. First bind the radio button component via binding to a variable in the view, then reference its UIInput#getValue() method in the required attribute.
<h:selectOneRadio id="rvmMeetingOption" binding="#{rvmMeetingOption}"
readonly="#{wipMB.rvmMeetingOptionReadOnly}"
value="#{wipMB.requirementsMeeting}"
disabled="#{wipMB.rvmMeetingOptionDisabled}">
<f:selectItem itemLabel="No" itemValue="0"></f:selectItem>
<f:selectItem itemLabel="Yes" itemValue="1" ></f:selectItem>
</h:selectOneRadio>
<p:calendar id="rvmDate"
readonly="#{wipMB.rvmMeetingDateReadOnly}"
disabled="#{wipMB.rvmMeetingDateDisabled}"
readonlyInput="true"
navigator="true" mode="popup"
pattern="dd/MM/yyyy"
value="#{wipMB.rvmDate}"
effect="explode"
yearRange="1900:2500"
style="margin-left:5px"
required="#{rvmMeetingOption.value == 1}">
</p:calendar>
use
UIInput uiCalendar = (UIInput) component.getParent().findComponent("rvmDate");
Date test = uiCalendar.getValue();
if(test==null){
throw ValidatorException
}
test will then have the date filled in or will be null when nothing is chosen in teh date field

Duplicate Id. JSF

I have a problem with JSF. Can anyone say why this doesn't work?
<h:selectOneListbox
id="lang" size="5"
value="#{MbInstitution.node.lang}"
valueChangeListener="#{MbInstitution.changeLanguage}"
rendered="#{MbInstitution.view}"
>
<a4j:support event="onchange" reRender="shortDesc, fullDesc"/>
<f:selectItems value="#{MbInstitution.languagesByInstitute}"/>
</h:selectOneListbox>
<h:selectOneListbox
id="lang" size="5"
disabled="#{!MbInstitution.managingNew}"
value="#{MbInstitution.node.lang}"
rendered="#{!MbInstitution.view}"
>
<f:selectItems value="#{MbInstitution.availableLanguages}"/>
</h:selectOneListbox>
It says: "duplicate Id for a component instForm:lang"
I know that i have 2 elements with same Id, but one is rendered only when another isn't. So, i didn't think it would be a problem. Actually it's not a big problem at all as i don't need this id, but what if i needed then what would i do?
Your problem is that these two components are part of the JSF Component tree for this page. And even if they cannot be displayed at the same time, they share the same ID, which is not allowed by JSF.
I see three solutions to solve your problem:
First solution: Define two differents ID
Second solution: You can, as explained by Wayne Young, use a NamingContainer, which will prefix their ID by the ID of the NamingContainer.
Third solution: Use only one <h:selectOneListbox/> and then make the difference in the Java code.
<h:selectOneListbox id="lang" size="5" disabled="#{!MbInstitution.managingNew}" value="#{MbInstitution.node.lang}" valueChangeListener="#{MbInstitution.changeLanguage}">
<a4j:support event="onchange" reRender="shortDesc, fullDesc" rendered="#{MbInstitution.view}"/>
<f:selectItems value="#{MbInstitution.languages}"/>
</h:selectOneListbox>
Java code:
public List<SelectItem> getLanguage() {
if (isView()) {
return getLanguagesByInstitute();
} else {
return getAvailableLanguages();
}
}
public void changeLanguage(ValueChangeEvent evt) {
if (!isView()) {
return;
}
...
}
You'll have to use a different ID or put it in another naming container.
The Javadoc for UIComponent.setId() says:
The specified identifier must be
unique among all the components
(including facets) that are
descendents of the nearest ancestor
UIComponent that is a NamingContainer,
or within the scope of the entire
component tree if there is no such
ancestor that is a NamingContainer.

How to display my application's errors in JSF?

In my JSF/Facelets app, here's a simplified version of part of my form:
<h:form id="myform">
<h:inputSecret value="#{createNewPassword.newPassword1}" id="newPassword1" />
<h:message class="error" for="newPassword1" />
<h:inputSecret value="#{createNewPassword.newPassword2}" id="newPassword2" />
<h:message class="error" for="newPassword2" />
<h:commandButton value="Continue" action="#{createNewPassword.continueButton}" />
</h:form>
I'd like to be able to assign an error to a specific h:message tag based on something happening in the continueButton() method. Different errors need to be displayed for newPassword and newPassword2. A validator won't really work, because the method that will deliver results (from the DB) is run in the continueButton() method, and is too expensive to run twice.
I can't use the h:messages tag because the page has multiple places that I need to display different error messages. When I tried this, the page displayed duplicates of every message.
I tried this as a best guess, but no luck:
public Navigation continueButton() {
...
expensiveMethod();
if(...) {
FacesContext.getCurrentInstance().addMessage("newPassword", new FacesMessage("Error: Your password is NOT strong enough."));
}
}
What am I missing? Any help would be appreciated!
FacesContext.addMessage(String, FacesMessage) requires the component's clientId, not it's id. If you're wondering why, think about having a control as a child of a dataTable, stamping out different values with the same control for each row - it would be possible to have a different message printed for each row. The id is always the same; the clientId is unique per row.
So "myform:mybutton" is the correct value, but hard-coding this is ill-advised. A lookup would create less coupling between the view and the business logic and would be an approach that works in more restrictive environments like portlets.
<f:view>
<h:form>
<h:commandButton id="mybutton" value="click"
binding="#{showMessageAction.mybutton}"
action="#{showMessageAction.validatePassword}" />
<h:message for="mybutton" />
</h:form>
</f:view>
Managed bean logic:
/** Must be request scope for binding */
public class ShowMessageAction {
private UIComponent mybutton;
private boolean isOK = false;
public String validatePassword() {
if (isOK) {
return "ok";
}
else {
// invalid
FacesMessage message = new FacesMessage("Invalid password length");
FacesContext context = FacesContext.getCurrentInstance();
context.addMessage(mybutton.getClientId(context), message);
}
return null;
}
public void setMybutton(UIComponent mybutton) {
this.mybutton = mybutton;
}
public UIComponent getMybutton() {
return mybutton;
}
}
In case anyone was curious, I was able to figure this out based on all of your responses combined!
This is in the Facelet:
<h:form id="myform">
<h:inputSecret value="#{createNewPassword.newPassword1}" id="newPassword1" />
<h:message class="error" for="newPassword1" id="newPassword1Error" />
<h:inputSecret value="#{createNewPassword.newPassword2}" id="newPassword2" />
<h:message class="error" for="newPassword2" id="newPassword2Error" />
<h:commandButton value="Continue" action="#{createNewPassword.continueButton}" />
</h:form>
This is in the continueButton() method:
FacesContext.getCurrentInstance().addMessage("myForm:newPassword1", new FacesMessage(PASSWORDS_DONT_MATCH, PASSWORDS_DONT_MATCH));
And it works! Thanks for the help!
You also have to include the FormID in your call to addMessage().
FacesContext.getCurrentInstance().addMessage("myform:newPassword1", new FacesMessage("Error: Your password is NOT strong enough."));
This should do the trick.
Regards.
Remember that:
FacesContext context = FacesContext.getCurrentInstance();
context.addMessage( null, new FacesMessage( "The message to display in client" ));
is also valid, because when null is specified as first parameter, it is applied to the whole form.
More info: coreservlets.com //Outdated
JSF is a beast. I may be missing something, but I used to solve similar problems by saving the desired message to a property of the bean, and then displaying the property via an outputText:
<h:outputText
value="#{CreateNewPasswordBean.errorMessage}"
render="#{CreateNewPasswordBean.errorMessage != null}" />
Found this while Googling. The second post makes a point about the different phases of JSF, which might be causing your error message to become lost. Also, try null in place of "newPassword" because you do not have any object with the id newPassword.
I tried this as a best guess, but no luck:
It looks right to me. Have you tried setting a message severity explicitly? Also I believe the ID needs to be the same as that of a component (i.e., you'd need to use newPassword1 or newPassword2, if those are your IDs, and not newPassword as you had in the example).
FacesContext.getCurrentInstance().addMessage("newPassword1",
new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error Message"));
Then use <h:message for="newPassword1" /> to display the error message on the JSF page.
Simple answer, if you don't need to bind it to a specific component...
Java:
FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Authentication failed", null);
FacesContext context = FacesContext.getCurrentInstance();
context.addMessage(null, message);
XHTML:
<h:messages></h:messages>

Resources