How to make JSF validator, valueChangeListener and actionListener be called once but not multiple times when binding and View scope are used

China☆狼群 提交于 2020-01-06 08:49:06

问题


The JSF component uses binding and backing bean is of View scope. The component have validator and valueChangeListener set. And when component's value is changed partial request is sent to server. And validator and valueChangListener are called many times but not once within request.

How to make them to be called once during request?

If remove binding the methods are called correctly once.

But is it possible to do not remove binding and make listeners be called once?

Sample of used code is next:

<h:inputText id="txt"
             validator="#{myBean.validateValue}"
             valueChangeListener="#{myBean.valueChanged}"
             binding="#{myBean.txtInput}">
   <f:ajax event="valueChange"
           execute="@this"
           render="@this"/>
</h:inputText>

@ViewScoped
@ManagedBean
public class MyBean {

private HtmlInputText txtInput;

public void valueChanged(ValueChangeEvent ve) {
    ...
}

public void validateValue(FacesContext context, UIComponent component, Object value) {
    ...
}

public HtmlTextInput getTxtInput() {
    return txtInput;
}

public void setTxtInput(HtmlTextInput txtInput) {
    this.txtInput = txtInput;
}    

}

The same issue takes place for actionListener and commandLink component if it uses binding and backing bean has View scope.


回答1:


The possible solution could be just to override class which is used by UIInput and UICommand to store validators and listeners. The class is javax.faces.component.AttachedObjectListHolder.

This class straightforward add new listener to backing list not checking if the same listener already is there. Thus the solution is to check if listener exists and not to add it then.

So take javax.faces.component.AttachedObjectListHolder from jsf-api-2.1.<x>-sources.jar and add it to your project to the corresponding package. Replace method add with such one:

void add(T attachedObject) {
    boolean addAttachedObject = true;

    if (attachedObject instanceof MethodExpressionActionListener
        || attachedObject instanceof MethodExpressionValueChangeListener
        || attachedObject instanceof MethodExpressionValidator) {

        if (attachedObjects.size() > 0) {
            StateHolder listener = (StateHolder) attachedObject;
            FacesContext context = FacesContext.getCurrentInstance();
            Object[] state = (Object[]) listener.saveState(context);
            Class<? extends StateHolder> listenerClass = listener.getClass();

            for (Object tempAttachedObject : attachedObjects) {
                if (listenerClass.isAssignableFrom(tempAttachedObject.getClass())) {
                    Object[] tempState = (Object[]) ((StateHolder) tempAttachedObject).saveState(context);
                    if (((MethodExpression) state[0]).getExpressionString().equals(((MethodExpression)tempState[0]).getExpressionString())) {
                        addAttachedObject = false;
                        break;
                    }
                }
            }
        }
    }

    clearInitialState();
    if (addAttachedObject) {
        attachedObjects.add(attachedObject);
    }
}

After that validators, valueChangeListeners and actionListeners will be triggered only once even when component binding and "View", "Session" or "Application" scopes are used.



来源:https://stackoverflow.com/questions/26183172/how-to-make-jsf-validator-valuechangelistener-and-actionlistener-be-called-once

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!