我可以将 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>
属性的其他属性,例如如 execute
、render
、delay
等)。您还应该针对标准 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
我已将 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>
属性的其他属性,例如如 execute
、render
、delay
等)。您还应该针对标准 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