Subscribing to PostValidationEvent of dynamicaly created child component

允我心安 提交于 2019-12-12 01:27:57

问题


For a forms framework in which I like to use JSF as the real UI frontend, I am searching for a way that a parent component gets informed if in a child component the value is changed. The facelet of a basic 'control' looks like this(body/head omitted since no-one can run it anyway without a dozen classes):

<xf:input ref="/my/xpath/value">
    <xf:label>Label</xf:label>
</xf:input>

The xf:input component which I developed, dynamically creates a real ui component (PrimeFaces ones) based on the type of the value that ref="/my/xpath/value" points to. This real ui component is created in a preRenderView event like is done in this example. It is handled in the following method in the 'parent' control

@Override
public void processEvent(SystemEvent event) throws AbortProcessingException {

    FacesContext context = FacesContext.getCurrentInstance();

    if (!context.isPostback()) {
        control = createControl(context);
//context.getApplication().unsubscribeFromEvent(PostValidateEvent.class, getControl().getClass(), this);
        control.subscribeToEvent(PostValidateEvent.class, this);
    }
}

The actual controls all have an programmatically added ajax handler added to it, which makes it possible to just process the specific input ('implicit ajax'). Default JSF component validations are normally applied and this all works great.

The issue/challenge is that in this 'wrapper' component I'd like to be informed of value changes after the validation. My first idea was to us the subscribeEvent on the dynamically added control like this:

control.subscribeToEvent(PostValidateEvent.class, this);

The subscribing works, but on the postback, an NPE is thrown in the UIComponent (Mojarra 2.2.9) because the wrapped is null in the following method

public boolean isListenerForSource(Object component) {

    if (wrapped instanceof SystemEventListener) {
        return ((SystemEventListener) wrapped).isListenerForSource(component);
    } else {
        return instanceClass.isAssignableFrom(component.getClass());
    }

}

This might be because the actual component seems to be newly created when the data is submitted en hence the 'subscription' is lost.

Registering on the ViewRoot does not work since the source of the event is always the ViewRoot and registering on the Application is plain wrong.

It might be that I'm looking for a solution in the wrong direction but for now I'm clueless. Keep in mind that I have no direct control over the created ui controls, nor do I want to override their renderers if I can prevent to. So signalling the parent from the child control is not an option to.

Other things I tried:

  • Using valueChangeListeners but that did not work either with lots of other problems (including ways to make it extensible)
  • Using composite components with binding but that failed including them dynamically, requiring naming containers that conflict with the id's required by the rest of the framework, the positions of labels, hints and alerts in the xhtml and/or resulting dom
  • Taghandlers to manipulate the tree when creating them

This all is with Mojarra up to 2.2.9 (did not check newer yet or MyFaces)


回答1:


Adding the component in the PreRenderViewEvent works nicely. The thing is that you do not seem to be able to have subscriptions to events survive a request. The actual components are recreated (in the RestoreViewPhase I assume, did not check) and then the subscription to the event is still there, just the 'wrapped' context where it should be called is empty.

Adding the PostValidationEvent event in the PostRestoreStateEvent of this specific component (it is the only one in the FacesContext.getCurrentInstance().getPartialViewContext().getExecuteIds()) makes it fire as mentioned in the comments. The trick (hack/workaround/...) to get rid of the NPE in the next request is to actually remove the event again.

((UIComponent) event.getSource()).unsubscribeFromEvent(PostValidateEvent.class, this);

I'll try to create an example without any PrimeFaces or OmniFaces and see what happens then since they both seem to be wrappers around the context and I want to make sure they are not the cause of the behaviour.



来源:https://stackoverflow.com/questions/29156426/subscribing-to-postvalidationevent-of-dynamicaly-created-child-component

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