I have a jsf composite component implemented from two p:calendar components.
The idea is when the first calendar is selected, the value of the second calendar need to be reset. There is a problem when the validation takes place, and the reset of the second calendar is not performed.
After reading posts I decided to use EditableValueHolder in my validator.
I have custom validator: in which I added the following code:
#Override
public void validate(FacesContext fc, UIComponent uic, Object o) throws ValidatorException {
//....
resetValues(fc);
}
public void resetValues(FacesContext fc) {
PartialViewContext partialViewContext = fc.getPartialViewContext();
Collection<String> renderIds = partialViewContext.getRenderIds();
UIComponent input;
UIViewRoot viewRoot = fc.getViewRoot();
for (String renderId : renderIds) {
input = viewRoot.findComponent(renderId);
if (input.isRendered() && input instanceof EditableValueHolder) {
EditableValueHolder editableValueHolder = (EditableValueHolder) input;
editableValueHolder.setSubmittedValue(null);
editableValueHolder.setValue(null);
editableValueHolder.setValid(true);
editableValueHolder.setLocalValueSet(false);
}
}
}
After debug I can see that each code line is passed, but nothing is happening on jsf side.
This is not the right moment to reset the values. They will be overridden anyway for the current component after the validate() method leaves and also for the second calendar once it get validated. You need to perform the reset somewhere after the update model values phase, preferably before the invoke action phase, so that you've chance to change the model value in an action(listener) method. You could use an ActionListener or a PhaseListener for this.
By the way, the JSF utility library OmniFaces has a reuseable solution for this in flavor of ResetInputAjaxActionListener.
Related
I'm working on an existing JSF component where the encodeEnd method ends with:
// popComponentFromEL(context);
The Javadoc for UIComponent#popComponentFromEL(FacesContext context) tells me:
Pop the current UIComponent from the FacesContext attributes map so that the previous UIComponent, if any, becomes the current component.
When and why would you need or want that?
I've found that none of the other components in the same library are using it.
That's the counterpart of pushComponentToEL() whose Javadoc explains this more elaborately.
pushComponentToEL
public final void pushComponentToEL(FacesContext context, UIComponent component)
Push the current UIComponent this to the FacesContext attribute map using the key CURRENT_COMPONENT saving the previous UIComponent associated with CURRENT_COMPONENT for a subsequent call to popComponentFromEL(javax.faces.context.FacesContext).
This method and popComponentFromEL() form the basis for the contract that enables the EL Expression "#{component}" to resolve to the "current" component that is being processed in the lifecycle. The requirements for when pushComponentToEL() and popComponentFromEL() must be called are specified as needed in the javadoc for this class.
After pushComponentToEL() returns, a call to getCurrentComponent(javax.faces.context.FacesContext) must return this UIComponent instance until popComponentFromEL() is called, after which point the previous UIComponent instance will be returned from getCurrentComponent()
Basically, this approach
public void encodeXxx(FacesContext context) {
try {
pushComponentToEL(context, this);
// ...
}
finally {
popComponentFromEL(context);
}
}
allows you during the // ... process to grab this component using #{component} in EL, or UIComponent#getCurrentComponent() in a managed bean.
One of well known examples is this construct:
<h:inputText ... styleClass="#{component.valid ? 'valid' : 'error'}" />
where #{component.valid} basically refers UIInput#isValid().
I'm trying to understand the working principles of UIInput component and EL-expressions in JSF. Here is the code fragment of UIInput component source code:
public void updateModel(FacesContext context) {
if (context == null) {
throw new NullPointerException();
}
if (!isValid() || !isLocalValueSet()) {
return;
}
ValueExpression ve = getValueExpression("value");
if (ve != null) {
Throwable caught = null;
FacesMessage message = null;
try {
ve.setValue(context.getELContext(), getLocalValue()); //1
setValue(null);
setLocalValueSet(false);
}
//catch clause
Now, I have a simple bean and the input component binded to its property:
<h:inputText value="#{myBean.hello}" />
#ManagedBean
#SessionScoped
public class MyBean{
private String hello;
//getter,setter
}
As far as I understand, the updateModel method is called by the inherited from the UIComponentBase class processUpdates(FacesContext) method which is a standard callback for the Update Model Values phase. So, after setting breakpoint at //1 and performing step next the flow's stopped at the corresponding bean property setter method.
My question is about of purpose of javax.el.ValueExpression. Is it resposible for all interatctions (incapsulates) between the component class and the bean's property in order for getting/setting values to bean's properties?
EL is like a "path" and is needed to navigate the bean. The UIInput is the MVC "view" to the MVC "model" represented by your bean. EL links the two together. It only encapsulates the traversal itself. It identifies the subject of the interaction, but the interactions are defined by the component class. Compare h:outputText to h:inputText: both basically take a value="#{some.el}" attribute; the fact that one allows the user to modify it is a fact about the component, not the path to the bean property itself.
Some components define attributes that should be populated by references to methods rather than bean properties, but again, the EL is just the traversal—invoking those methods is something the component does. And in that case you would be dealing with a MethodExpression instead of a ValueExpression.
The JSF lifecycle dictates that things happen in a certain overall order, but the component author still has ample opportunity to be creative (or wreak havoc) by doing things their own way.
I need to add a component (UIParameter) to a HtmlCommandLink component dinamically through a Phase Listener.
What I want to achieve is that every element <h:link outcome="out"> renders as <a href="out_url_parsed + ?param=paramvalue">.Where "param" is my component.
I've tried using this
private void addElement(final PhaseEvent event, final Class clazz, final UIComponent component) {
final FacesContext fcontext = event.getFacesContext();
UIViewRoot root = fcontext.getViewRoot();
if (root == null) {
return;
}
root.visitTree(new FullVisitContext(fcontext), new VisitCallback() {
#Override
public VisitResult visit(VisitContext context, UIComponent target) {
if (clazz.isInstance(target)) {
LOGGER.info("Element Found");
UIParameter parameter = new UIParameter();
parameter.setValue("willberonadom");
parameter.setId("sessiontoken");
target.getChildren().add(parameter);
}
return VisitResult.ACCEPT;
}
});
}
But it's not working. The element is actually found on the tree but the UIParameter does not render.
I've found that the UIViewRoot only has child elements after RENDER_RESPONSE phase. So i think this is why my added element is not rendered at the end of the process.
I'm sure I can add this param editing the views but I don't want to do that since it must be present on all h:link in the application and must be present on any other new added too. So I consider this as a better approach to avoid missing tags
On a similar case I've managed to add input hidden elements to every form on view with this code...
HtmlInputHidden hiddenToken = new HtmlInputHidden();
hiddenToken.setId("sessiontoken");
hiddenToken.setValue("willberandom");
hiddenToken.setRendered(true);
root.addComponentResource(event.getFacesContext(), hiddenToken,"form");
But it doesn't work on anchor tags
There are several mistakes:
You want to add a parameter to a HtmlCommandLink component which represents <h:commandLink>, but you're giving an example with <h:link>, which is represented by HtmlOutcomeTargetLink. What exactly do you want?
A PhaseListener on beforePhase() of RENDER_RESPONSE may be too late on GET requests which would only build the view for the first time during render response. At the moment your PhaseListener runs, the UIViewRoot would have no children at all. You'd better hook on view build time instead. For that, a SystemEventListener on PostAddToViewEvent is the best suitable.
You're setting the parameter name as an id instead of name. Use UIParameter#setName() instead of UIParameter#setId().
Provided that you actually meant to add them to <h:link> components, then here's a kickoff example how you can achieve that with a SystemEventListener.
public class YourSystemEventListener implements SystemEventListener {
#Override
public boolean isListenerForSource(Object source) {
return source instanceof HtmlOutcomeTargetLink;
}
#Override
public void processEvent(SystemEvent event) throws AbortProcessingException {
UIParameter parameter = new UIParameter();
parameter.setName("sessiontoken");
parameter.setValue("willberonadom");
((UIComponent) event.getSource()).getChildren().add(parameter);
}
}
(if you actually want to apply them on <h:commandLink> as well, just extend the isListenerForSource() check with a || source instanceof HtmlCommandLink)
In order to get it to run, register it as follows in faces-config.xml:
<application>
<system-event-listener>
<system-event-listener-class>com.example.YourSystemEventListener</system-event-listener-class>
<system-event-class>javax.faces.event.PostAddToViewEvent</system-event-class>
</system-event-listener>
</application>
I have a "new item" form that requires a list of dates, with the following components:
A <rich:calendar> input;
A <a4j:commandButton> that adds the chosen date to a List<Date> chosenDates in the backing bean;
A <rich:dataTable> with it's value set to the List<Date> chosenDates attribute;
A <a4j:commandButton> per dataTable row that removes it's date from theList<Date> chosenDates;
How to validate (JSF's validation phase) the size of the chosenDates list on form submit (creation process)?
RichFaces 4, JSF 2.1 (Mojarra).
I'd advise a cleaner approach with a JSF PhaseListener. The JSF processing will stop skip ahead the other phases if validation fails. Create a PhaseListener that will inspect the size of your list during the validations phase as against during the model update/invoke action phase. Try something like this
Create a phase listener for the validations phase
public class TestPhaseListener implements PhaseListener {
#Override
public void afterPhase(PhaseEvent event) {
throw new UnsupportedOperationException("Not supported yet.");
}
#Override
public void beforePhase(PhaseEvent event) {
if(event.getPhaseId().equals(PhaseId.PROCESS_VALIDATIONS)){
FacesContext ctx = event.getFacesContext();
YourBeanClass theBeanClass = ctx.getApplication().evaluateExpressionGet(ctx, "#{someBean}", YourNeanClass.class); //obtain a reference to the backing bean containing the list
/*
inspect the size of the list here and based on that throw the exception below
*/
throw new ValidatorException(new FacesMessage("Too many dates","Too Many Dates"));
}
}
#Override
public PhaseId getPhaseId() {
throw new UnsupportedOperationException("Not supported yet.");
}
}
Register your new listener in the faces_config.xml file
<lifecycle>
<phase-listener>your.package.structure.TestPhaseListener</phase-listener>
</lifecycle>
EDIT: Based on your comment, as an alternative, you can hook into the component's lifecycle using the <f:event/> tag and the preValidate or postValidate events (depending on your preference)
A listener tag to your component
<rich:dataTable>
<f:event type="preValidate" listener="#{yourBean.listener}"/>
</rich:dataTable>
Define a listener method in your backing bean to run per your defined event. The method signature must take an argument of type ComponentSystemEvent
public void preCheck(ComponentSystemEvent evt){
//You're in your backing bean so you can do pretty much whatever you want. I'd advise you mark the request as validation failed and queue FacesMessages. Obtain a reference to FacesContext and:
facesContext.validationFailed();
}
Do something like:
#{yourBean.chosenDates.size()}
I suppose you have a getter called getChosenDates which returns the chosenDates list.
Regarding your "validation concerns":
You can create a Validate method in your bean and return list of ValidationMessages. A sample is below, one that i used in my code.
public List<ValidationMessage> validate() {
List<ValidationMessage> validations = new ArrayList<ValidationMessage>();
int curSampleSize = sampleTable.getDataModel().getRowCount();
if(getNumberOfSamples() != null) {
size += getNumberOfSamples();
} else {
validations.add(new ValidationMessage("Please enter the no of samples to continue."));
return validations;
}
return validations;
}
Then, on submit you can check if you have any ValidationMessages as follows:
List<ValidationMessage> errs = validate();
if(errs.size()>0) {
FacesValidationUtil.addFacesMessages(errs);
return null;
}
Hope this helps!
I have an issue with the attributes values of a validator component.
Apparently the validator is created when I first visit a page.
Please see my code below:
<h:inputText value="#{qsetting.value}" rendered="#{qsetting.dataType=='Double'}">
<mw:validateRange min="#{qsetting.minValue}" max="#{qsetting.maxValue}" />
</h:inputText>
The inputText component is rerendered through ajax but apparently, including the value that is displayed.
Unfortunately, the qsetting.minValue and qsetting.maxValue are not refreshed, causing my validator to not work correctly.
Is there a possibility to refresh the validator, to make sure it re-retrieves its attributes or to just create a new instance of the validator?
The validator class itself is currently implementing "Validator, Serializable".
Also, I'm using jsf1.2 with facelets...
Thanks,
Steven
I've hit this problem in a non-ajax environment a few times over the years, and hit it again today. The addition of Ajax doesn't really change anything since a validator attribute is never evaluated again once the page is initially built, ajax or otherwise.
The only solution I've come up with is to set the validator attribute to a validator expression, then evaluate that expression inside the validate method.
One other issue I hit (also with JSF 1.2 and Facelets) is that not all EL variables worked. I had to use a static managed bean as the root of my expression to access the value. A facelet ui:param value as a root would not work. I haven't tested to see what else may not correctly evaluate. This could be due to another bug in the design of JSF itself. See http://myfaces.apache.org/core12/myfaces-api/apidocs/javax/faces/context/FacesContext.html#getELContext%28%29.
For example, instead of:
max="#{qsetting.maxValue}"
use
maxExpression="qsetting.maxValue"
Then
public String getMax(FacesContext context) {
Application app = context.getApplication();
ExpressionFactory exprFactory = app.getExpressionFactory();
ValueExpression ve = exprFactory.createValueExpression(context.getELContext(),
"#{" + getMaxExpression() + "}",
String.class);
Object result = ve.getValue(context.getELContext());
return (String)result;
}
public String getMaxExpression() {
return this.maxExpression;
}
public void setMaxExpression(String maxExpression) {
this.maxExpression = maxExpression;
}
//// StateHolder
public boolean isTransient() {
return isTransient;
}
public void setTransient(boolean newTransientValue) {
isTransient = newTransientValue;
}
public Object saveState(FacesContext context) {
Object[] state = new Object[1];
state[0] = maxExpression;
return state;
}
public void restoreState(FacesContext context, Object state) {
Object[] values = (Object[]) state;
maxExpression = (String) values[0];
}
UPDATE 2012-09-19:
After investigating how MyFaces Commons solves this problem, the better solution is to change the rules Facelets uses to evaluate validator and converter attribute expressions.
It basically comes down to adding a new validator or converter MetaRule which, when applied, checks to see if the attribute value is non-literal. If it is non-literal, call a special method on your validator or converter which passes in the value expression rather than the current value.
http://svn.apache.org/viewvc/myfaces/commons/trunk/myfaces-commons-validators/src/main/java/org/apache/myfaces/commons/validator/_ValidatorRule.java?view=markup
The validator at that point needs to store the value expression as state and evaluate it when needed. MyFaces commons provides all of the complicated infrastructure to make this happen generically, but you could dump all of that and write a simple custom rule and directly manage the ValueExpression yourself, similar to what I originally posted.