我可以将 panelGroup 链接到没有 f:ajax 侦听器的侦听器吗?

Can I have a panelGroup linked to a listener without f:ajax listener?

我已将 f:ajax 标记替换为不放置内联脚本的自制解决方案。它对 actionButton 非常有效。但是我无法让它在 panelGroup 上为听众工作。原因是它没有指定 ajax 请求产生的 bean 目标方法应该是什么。换句话说,使用 commandButton 我可以在 action 中指定目标 bean 方法,但是 panelGroup 没有这样的属性;因为我不想使用 f:ajax listener,所以我想替换它。

 <h:commandButton data-widget="jsfajax" value="ajax" action="#{someAction}"/>
$(document).ready(function(){   
    (function(widgets){
        document.body.addEventListener("click", function(e) {
               var w = e.target.getAttribute("data-widget");
               if(w){
                   e.preventDefault();
                   widgets[w](e.target);
               }
            });
    })(new Widgets);
});
function Widgets(){
    this.jsfajax =  function jsfajax(elem){ 
            if(elem.id == ""){
            elem.id = elem.name;
        }
       mojarra.ab(elem,"click",'action','@form',0);  
     }
}

这有效。

但这显然不会(它会但不会调用任何东西):

 <h:panelGroup>
    <f:passThroughAttribute name="data-widget" value="jsfajax"/>
    Click here
 </h:panelGroup>

但这确实 :

 <h:panelGroup>
    <f:ajax event="click" listener="#{someAction}"/>
    Click here
 </h:panelGroup>

这两个 panelGroup 都会产生相同的 HTML 输出,所以我假设它是 jsf 容器,"remembers" 对该 panelGroup 的点击被 link 编辑到 #{someAction}.

我想做的是在不使用 f:ajax listener 的情况下重新创建 link。目前,我必须使用不太优雅的隐藏命令按钮。 所以也许一个复合组件 panelGroup 可以保存 "action link",我不知道。

您想要实现的功能只能在 UICommand 个组件上实现,而不能在 ClientBehaviorHolder 个组件上实现。一种解决方案是创建一个扩展 HtmlCommandLink 的自定义组件,它呈现 <div> 而不是 <a> 并像这样使用它 <your:div action="#{bean.action}">.

最理想的解决方案是替换标准渲染器。例如。 <h:panelGorup>:

<render-kit>
    <renderer>
        <component-family>javax.faces.Panel</component-family>
        <renderer-type>javax.faces.Group</renderer-type>
        <renderer-class>com.example.YourPanelGroupRenderer</renderer-class>
    </renderer>
</render-kit>

基本上,那些渲染器应该跳过渲染 <f:ajax> 相关的 on* 属性,而是渲染您的 data-widget 属性(最好还有代表现有 <f:ajax> 属性的其他属性,例如如 executerenderdelay 等)。您还应该针对标准 API 而不是特定于 Mojarra 的 API 进行编程。 IE。直接使用 jsf.ajax.request() 而不是 mojarra.ab() 快捷方式。

通过这种方式,您可以使视图保持一致并符合 JSF 标准。您和未来的开发人员在编写 JSF 代码时甚至不需要 learn/think 关于“专有” API。您只需继续使用 <h:panelGroup><f:ajax>。您只需通过 webapp 中的 JAR 插入自定义渲染和脚本即可。该 JAR 甚至可以在所有其他现有的 JSF 应用程序上重用。它甚至可能会流行起来,因为内联脚本确实被认为是不好的做法。

这只是一些代码,对于初学者来说不一定是微不足道的。

另一种方法是将标准响应编写器替换为自定义响应编写器,其中您覆盖 writeAttribute() 并检查属性名称是否以 on 开头,然后按照您在头脑。例如。解析它并编写不同的属性。这是一个启动示例,它也可以识别 <h:panelGroup><f:ajax>.

public class NoInlineScriptRenderKitFactory extends RenderKitFactory {

    private RenderKitFactory wrapped;

    public NoInlineScriptRenderKitFactory(RenderKitFactory wrapped) {
        this.wrapped = wrapped;
    }

    @Override
    public void addRenderKit(String renderKitId, RenderKit renderKit) {
        wrapped.addRenderKit(renderKitId, renderKit);
    }

    @Override
    public RenderKit getRenderKit(FacesContext context, String renderKitId) {
        RenderKit renderKit = wrapped.getRenderKit(context, renderKitId);
        return (HTML_BASIC_RENDER_KIT.equals(renderKitId)) ? new NoInlineScriptRenderKit(renderKit) : renderKit;
    }

    @Override
    public Iterator<String> getRenderKitIds() {
        return wrapped.getRenderKitIds();
    }

}
public class NoInlineScriptRenderKit extends RenderKitWrapper {

    private RenderKit wrapped;

    public NoInlineScriptRenderKit(RenderKit wrapped) {
        this.wrapped = wrapped;
    }

    @Override
    public ResponseWriter createResponseWriter(Writer writer, String contentTypeList, String characterEncoding) {
        return new NoInlineScriptResponseWriter(super.createResponseWriter(writer, contentTypeList, characterEncoding));
    }

    @Override
    public RenderKit getWrapped() {
        return wrapped;
    }

}
public class NoInlineScriptResponseWriter extends ResponseWriterWrapper {

    private ResponseWriter wrapped;

    public NoInlineScriptResponseWriter(ResponseWriter wrapped) {
        this.wrapped = wrapped;
    }

    @Override
    public ResponseWriter cloneWithWriter(Writer writer) {
        return new NoInlineScriptResponseWriter(super.cloneWithWriter(writer));
    }

    @Override
    public void writeAttribute(String name, Object value, String property) throws IOException {
        if (name.startsWith("on")) {
            if (value != null && value.toString().startsWith("mojarra.ab(")) {
                super.writeAttribute("data-widget", "jsfajax", property);
            }
        }
        else {
            super.writeAttribute(name, value, property);
        }
    }

    @Override
    public ResponseWriter getWrapped() {
        return wrapped;
    }

}

您可以自由发挥的最重要部分是最后一个代码段中的 writeAttribute() 方法。上面的启动示例只是盲目地检查 on* 属性值是否以 Mojarra 特定的 "mojarra.ab(" 开头,然后写入您的 data-widget="jsfajax"。换句话说,每个单曲(自然使用!)<f:ajax> 都将以这种方式重写。您可以继续以自然方式使用 <h:commandLink><f:ajax><h:panelGroup><f:ajax>。不要忘记处理其他 <f:ajax> 属性。

为了达到运行,请在faces-config.xml中进行如下注册:

<factory>
    <render-kit-factory>com.example.NoInlineScriptRenderKitFactory</render-kit-factory>
</factory>

您只需要考虑现有的特定于实现的细节(幸运的是只有两个:Mojarra 和 MyFaces)。

另请参阅:

  • How do I determine the renderer of a built-in component