The error "/resources/components/hostForm.xhtml #25,84 value="#{cc.attrs.host.hostName}": Target Unreachable, 'host' returned null" occurs after I added
<f:validateLength minimum="1" maximum="200"/>
to an inputText field in a custom tag. Without the validation everything works well.
<!-- INTERFACE -->
<composite:interface>
<composite:attribute name="host" />
<composite:attribute name="prefix" />
</composite:interface>
<!-- IMPLEMENTATION -->
<composite:implementation>
<h:panelGrid columns="3" columnClasses="titleCell">
<h:outputLabel for="#{cc.attrs.prefix}hostName" value="Host Name" />
<h:inputText id="#{cc.attrs.prefix}hostName" value="#{cc.attrs.host.hostName}">
<f:validateLength minimum="1" maximum="200"/>
<rich:validator />
</h:inputText>
<rich:message for="#{cc.attrs.prefix}hostName" />
</h:panelGrid>
</composite:implementation>
The field is declared as
#Named
#Produces
private Host newHost;
The custom tag invocation:
<my:hostForm prefix="c" host="#{newHost}"/>
What do I need to change to get this work with validation?
EDIT:
The Bean already has the #ConversationScoped annotation.
Adding a #ConversationScoped annotation to the fields leads to this error:
hostForm.xhtml #20,74 value="#{cc.attrs.host.id}": org.jboss.weld.exceptions.IllegalProductException: WELD-000052 Cannot return null from a non-dependent producer method: [field] #Named #ConversationScoped #Produces
Try adding a scope to it, probably Request or Conversation. What you have is the default scope which is essentially like creating one with new everytime you need an instance of it.
I moved the annotations from the attribute to a getter, and instantiate the object by myself. This works.
#Produces
#Named("newHost")
public Host getNewHost() {
if ( newHost == null ) {
newHost = new Host();
}
return newHost;
}
https://community.jboss.org/thread/179527
Related
I am using Mojarra 2.3.0.
I have a composite component which contains an inputText. I want to be able to validate this inputText using a FacesValidator. Furthermore I want to pass certain parameters to the validator.
My Composite component is defined this way:
<composite:interface>
...
<composite:editableValueHolder name="inputValidator" targets="value" />
...
</composite:interface>
<composite:implementation>
...
<h:panelGroup layout="block" styleClass="col-sm-10">
<h:inputText id="value"
disabled="#{cc.attrs.disabled}"
value="#{cc.attrs.value}"
styleClass="form-control"
required="#{cc.attrs.required}"
onchange="#{cc.attrs.onChange}"
/>
</h:panelGroup>
...
</composite:implementation>
I use the component this way and declared a f:validator and f:attribute (which should be passed to the validator)
<uiComps:field labelText="add language"
id="languages"
autocompleteValues="#{ocrEngineViewModel.getAllSupportedLanguages()}" >
<f:ajax event="onChange" listener="#{ocrEngineViewModel.updateSelectedLanguages}" render="newParameter:languages:value newParameter:usedLanguages" />
<f:validator validatorId="OcrLanguageValidator" for="inputValidator" />
<f:attribute name="allSupportedLanguages" value="#{ocrEngineViewModel.allSupportedLanguages}"/>
</uiComps:field>
<h:message for="languages" />
My validator looks like this:
#FacesValidator("OcrLanguageValidator")
public class OcrLanguageValidator implements Validator {
#Override
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
Object allSupportedLanguages = component.getAttributes().get("allSupportedLanguages");
throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "lala", "not good"));
}
}
There are two problems:
The parameter "allSupportedLanguages" (which is a List<String>) is null when I try to retrieve it in the validator.
The ValidationException which holds a FaceMessage is not displayed in the message area for the component. I don't see any information about the message on the page.
Update:
Instead of the composite component I put the code directly into my page.
<h:panelGroup layout="block" styleClass="col-sm-10">
<h:inputText id="value" styleClass="form-control">
<f:ajax event="change" listener="#{ocrEngineViewModel.updateSelectedLanguages}" render="newParameter:languages:value newParameter:usedLanguages" />/>
<f:validator validatorId="OcrLanguageValidator"/>
<f:attribute name="allSupportedLanguages" value="#{ocrEngineViewModel.allSupportedLanguages}"/>
</h:inputText>
</h:panelGroup>
This works and I see the attribute in the validator. So I guess the problem lies in the usage of the composite component.
Update2:
I was able to solve the validation error message problem. I placed the <h:message for="value"/> inside the composite component.
JSF 2.0 only allows you to validate the input on one field, like check to see if it's a certain length. It doesn't allow you to have a form that says, "enter city and state, or enter just a zip code."
How have you gotten around this? I'm only interested in answers that involve the validation phase of JSF. I'm not interested in putting validation logic in Managed Beans.
The easiest custom approach I've seen and used as far is to create a <h:inputHidden> field with a <f:validator> wherein you reference all involved components as <f:attribute>. If you declare it before the to-be-validated components, then you can obtain the submitted values inside the validator by UIInput#getSubmittedValue().
E.g.
<h:form>
<h:inputHidden id="foo" value="true">
<f:validator validatorId="fooValidator" />
<f:attribute name="input1" value="#{input1}" />
<f:attribute name="input2" value="#{input2}" />
<f:attribute name="input3" value="#{input3}" />
</h:inputHidden>
<h:inputText binding="#{input1}" value="#{bean.input1}" />
<h:inputText binding="#{input2}" value="#{bean.input2}" />
<h:inputText binding="#{input3}" value="#{bean.input3}" />
<h:commandButton value="submit" action="#{bean.submit}" />
<h:message for="foo" />
</h:form>
(please note the value="true" on the hidden input; the actual value actually doesn't matter, but keep in mind that the validator won't necessarily be fired when it's null or empty, depending on the JSF version and configuration)
with
#FacesValidator(value="fooValidator")
public class FooValidator implements Validator {
#Override
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
UIInput input1 = (UIInput) component.getAttributes().get("input1");
UIInput input2 = (UIInput) component.getAttributes().get("input2");
UIInput input3 = (UIInput) component.getAttributes().get("input3");
// ...
Object value1 = input1.getSubmittedValue();
Object value2 = input2.getSubmittedValue();
Object value3 = input3.getSubmittedValue();
// ...
}
}
If you declare the <h:inputHidden> after the to-be-validated components, then the values of the involved components are already converted and validated and you should obtain them by UIInput#getValue() or maybe UIInput#getLocalValue() (in case the UIInput isn't isValid()) instead.
See also:
Validator for multiple fields (JSF 1.2 targeted)
Alternatively, you can use 3rd party tags/components for that. RichFaces for example has a <rich:graphValidator> tag for this, Seam3 has a <s:validateForm> for this, and OmniFaces has several standard <o:validateXxx> components for this which are all showcased here. OmniFaces uses a component based approach whereby the job is done in UIComponent#processValidators(). It also allows customizing it in such way so that the above can be achieved as below:
<h:form>
<o:validateMultiple id="foo" components="input1 input2 input3" validator="#{fooValidator}" />
<h:inputText id="input1" value="#{bean.input1}" />
<h:inputText id="input2" value="#{bean.input2}" />
<h:inputText id="input3" value="#{bean.input3}" />
<h:commandButton value="submit" action="#{bean.submit}" />
<h:message for="foo" />
</h:form>
with
#ManagedBean
#RequestScoped
public class FooValidator implements MultiFieldValidator {
#Override
public boolean validateValues(FacesContext context, List<UIInput> components, List<Object> values) {
// ...
}
}
The only difference is that it returns a boolean and that the message should be specified as message attribute in <o:validateMultiple>.
Apache ExtVal was not mentioned here.
There are some cross validations in it (among other validations which might be useful):
https://cwiki.apache.org/confluence/display/EXTVAL/Property+Validation+Usage#PropertyValidationUsage-CrossValidation
I have a form contains with inputText and message component.
I want to set the error message from the backing bean but keep fail to do it.
Below is my html code:
<h:form id="formId">
<h:panelGrid id="repeater" columns="2">
<h:outputText value="#{msg['label.appeal.case.reference.no']}" />
<ui:repeat id="uirepeater" value="#{beanPage.list}" var="value" varStatus="status">
<h:panelGrid columns="2">
<p:inputText id="refNo" value="#{beanPage.list[status.index]}" />
<p:message for="refNo" display="text" />
</h:panelGrid>
</ui:repeat>
</h:panelGrid>
<p:commandButton id="btmAdd" actionListener="#{beanPage.addRow}" value="Add" update="#form" />
<p:commandButton id="btmSubmit" actionListener="#{beanPage.submit}" value="Submit" update="#form" />
</h:form>
below is backing bean code:
public void submit() {
FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_ERROR, "test", "test");
FacesContext.getCurrentInstance().addMessage(":formId:uirepeater:refNo", msg);
FacesContext.getCurrentInstance().addMessage(":formId:uirepeater:0:refNo", msg);
}
This will not work with ui:repeat.
The actual ID of the inputText will not be "refNo" as you might think.
The < ui:repeat > will ensure the uniqueness of the generated component's client ID by prepending it with the row index. It just renders the same component several times, not creating new components in the tree.
You probably need to use c:forEach, which generates several components in the tree.
For more info see:
https://rogerkeays.com/jsf-c-foreach-vs-ui-repeat
How can I set id of a component/tag inside ui:repeat
Try this
I use primefaces 4.0, ViewScope, Ajax request.
With p:component obtain the ClientId of p:messages atributte for="someComponent" and pass this value to bean through p:remoteCommand, later you can set the message from bean with ClientId.
In some element add this
onclick="rc([{name:'index',value:#{item.index}},{name:'msg1',value:'#{p:component('someComponent')}'}])"
Create p:remoteCommand to set the id
<p:remoteCommand process="#this" name="rc" action="#{bean.someMethod}"/>
public void someMethod(){
FacesContext context = FacesContext.getCurrentInstance();
Map<String,String> params = context.getExternalContext().getRequestParameterMap();
int index=Integer.parseInt(params.get("index"));
this.msg[index]=params.get("msg1");
}
Now you can set messages from bean:
FacesContext.getCurrentInstance().addMessage(this.msg[x], new FacesMessage(FacesMessage.SEVERITY_ERROR,null,"someMessage"));
The use case is calling a method on a JSF 2.x Backing Bean directly from a hyperlink (Non-Faces-Request). What is the best way to do this?
I imagine to do something like this:
The Link:
http://localhost/show.xhtml?id=30&backingbeanname=loaddata&method=load
The Backing Bean:
#Named (value = "loaddata")
public class DataLoader {
public void load(int id){ ... }
}
Use <f:viewParam> in the target view to set GET parameters as bean properties and use <f:event type="preRenderView"> to invoke an action on them.
In show.xhtml:
<f:metadata>
<f:viewParam name="id" value="#{bean.id}" required="true" />
<f:event type="preRenderView" listener="#{bean.load}" />
</f:metadata>
<h:message for="id" />
In managed bean:
private Integer id;
private Data data;
public void load() {
data = service.find(id);
}
Note that in the above example the URL http://localhost/show.xhtml?id=30 is sufficient. You can always set more parameters as bean properties and have one "God" bean which delegates everything, but that's after all likely clumsy.
Also note that you can just attach a Converter to the <f:viewParam> (like as you could do in <h:inputText>). The load() method is then most likely entirely superfluous.
<f:metadata>
<f:viewParam name="id" value="#{bean.data}"
converter="dataConverter" converterMessage="Bad request. Unknown data."
required="true" requiredMessage="Bad request. Please use a link from within the system." />
</f:metadata>
<h:message for="id" />
See also:
Communication in JSF 2 - Processing GET request parameters
What can <f:metadata>, <f:viewParam> and <f:viewAction> be used for?
In our current project we want to replace a <h:selectOneMenu> with Primefaces's <p:autocomplete>. The select items are a list of Pojos (JPA Entities). The difference to the given examples in primefaces showcases is, that we want the primary key property (id) of the entity as selection value, so it can be easily passed as view param:
<f:metadata>
<f:viewParam name="playerId" value="#{playerPreRenderViewListener.searchCriteria.playerId}" />
<f:viewParam name="year" value="#{playerPreRenderViewListener.searchCriteria.year}" />
</f:metadata>
<h:form>
<h:inputText value="#{playerPreRenderViewListener.searchCriteria.year}"/>
<p:autoComplete var="player" itemLabel="#{player.name}" itemValue="#{player.id}"
completeMethod="#{playerBean.completePlayer}" forceSelection="true"
converter="#{playerConverter}"
value="#{playerPreRenderViewListener.searchCriteria.playerId}">
</p:autoComplete>
<h:commandButton value="Submit" action="showTeam?faces-redirect=true&includeViewParams=true" />
</h:form>
Unfortunately the example above will lead to a PropertyNotFoundException:
itemLabel="#{player.name}": Property 'name' not found on type java.lang.Long'
The problem is that the var attribute is of Type Long and not Player. When using a simple <h:selectMenu> it works in conjunction with <f:selectItems>:
<f:selectItems var="player" value="#{playerBean.listPlayers}" itemLabel="#{player.name}" itemValue="#{player.id}" />
Does anybody know how to handle this issue?
You could add a backing bean method that returns the player belonging to the currently active playerId and set this currentPlayer as value attribute of your backing bean:
public Player getCurrentPlayer() {
// find player by playerId and return
}
And in the view:
<p:autoComplete var="player" itemLabel="#{player.name}" itemValue="#{player}"
completeMethod="#{playerBean.completePlayer}" forceSelection="true"
converter="#{playerConverter}"
value="#{playerPreRenderViewListener.currentPlayer}">