订阅动态创建的子组件的PostValidationEvent [英] Subscribing to PostValidationEvent of dynamicaly created child component

查看:166
本文介绍了订阅动态创建的子组件的PostValidationEvent的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于一个我喜欢使用JSF作为真正的UI前端的表单框架,我正在寻找一种方式来通知父组件,如果子组件中的值被更改了.基本控件"的方面看起来像这样(省略了身体/头部,因为没有人可以在没有十几个类的情况下运行它):

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>

我开发的xf:input组件根据ref="/my/xpath/value"指向的值的类型动态创建一个真正的ui组件(PrimeFaces组件).像在本示例中一样,在preRenderView事件中创建了这个真正的ui组件.在父级"控件中通过以下方法进行处理

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);
    }
}

所有实际控件都以编程方式添加了ajax处理程序,从而可以仅处理特定的输入(隐式ajax").通常会应用默认的JSF组件验证,并且所有这些都很好用.

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.

问题/挑战在于,在包装器"组件中,我想在验证后被告知价值变化.我的第一个想法是给我们这样动态添加的控件上的subscriptionEvent:

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);

订阅有效,但在回发中,由于wrapped在以下方法中为空,因此在UIComponent(Mojarra 2.2.9)中引发了NPE.

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.

ViewRoot上注册不起作用,因为事件的来源始终是ViewRoot,并且在Application上注册是完全错误的.

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.

可能是我在寻找错误方向的解决方案,但目前我一无所知.请记住,我无法直接控制已创建的ui控件,如果可以阻止的话,我也不想覆盖其渲染器.因此,从子控件向父控件发出信号不是一种选择.

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.

我尝试过的其他事情:

  • 使用valueChangeListeners,但在许多其他问题(包括使其可扩展的方法)上也无效
  • 使用具有绑定但无法动态包含它们的复合组件,要求命名容器的名称与框架其余部分所要求的ID,xhtml中的标签,提示和警报的位置和/或生成的dom冲突
  • 标记处理程序在创建它们时对其进行操作

Mojarra的最高版本为2.2.9(尚未检查较新的版本或MyFaces)

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

推荐答案

在PreRenderViewEvent中添加组件效果很好.事实是,您似乎无法在请求后保留对事件的订阅.重新创建了实际的组件(在我假设的RestoreViewPhase中,没有检查),然后该事件的订阅仍然存在,只是应在其中调用的包装"上下文为空.

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.

在此特定组件的PostRestoreStateEvent中添加PostValidationEvent事件(它是FacesContext.getCurrentInstance().getPartialViewContext().getExecuteIds()中的唯一事件),如注释中所述触发它.在下一个请求中摆脱NPE的诀窍(hack/workaround/...)是实际上再次删除该事件.

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);

我将尝试创建一个没有任何PrimeFaces或OmniFaces的示例,然后看看会发生什么,因为它们似乎都是上下文的包装器,我想确保它们不是造成这种行为的原因.

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.

这篇关于订阅动态创建的子组件的PostValidationEvent的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆