gpt4 book ai didi

ajax - 带有 的动态 ajax 导航

转载 作者:行者123 更新时间:2023-12-04 05:21:09 25 4
gpt4 key购买 nike

假设我想在我的应用程序中导航,并动态地包含不同的 facelet 页面。我有一个这样的命令链接:

<h:commandLink value="Link" action="#{navigation.goTo('someTest')}">
<f:ajax render=":content" />
</h:commandLink>

这就是我包含 facelet 的地方:
<h:form id="content">
<ui:include src="#{navigation.includePath}" />
</h:form>

导航类:
public class Navigation {
private String viewName;

public void goTo(String viewName) {
this.viewName = viewName;
}

public String getIncludePath() {
return resolvePath(viewName);
}
}

我见过类似的例子,但这当然行不通。如 ui:include是一个标记处理程序,包含发生在我的导航监听器被调用之前很久。包括旧小面,而不是新小面。到目前为止我明白了。

现在到了令人头疼的部分:如何基于 actionListener 动态地包含一个 facelet?我尝试在 preRender 事件中包含 facelet,并在 RENDER_RESPONSE 之前包含一个 phaseListener。两者都有效,但在事件监听器中,我不能包含一个包含其他 preRender 事件的 facelet,而在 phaseListener 中,在包含的 facelet 中单击几下后,我会得到重复的 Id。但是,检查组件树告诉我,根本没有重复的组件。也许这两个想法根本不好..

我需要一个解决方案,其中带有 ui:include 的页面或包含 facelet 的 Java 类,不必知道将包含的页面,也不必知道确切的路径。以前有人解决过这个问题吗?我该怎么做?

我正在使用 JSF 2.1 和 Mojarra 2.1.15

重现问题所需的只是这个 bean:
@Named
public class Some implements Serializable {
private static final long serialVersionUID = 1L;
private final List<String> values = new ArrayList<String>();

public Some() {
values.add("test");
}

public void setInclude(String include) {
}
public List<String> getValues() {
return values;
}
}

这在您的索引文件中:
<h:head>
<h:outputScript library="javax.faces" name="jsf.js" />
</h:head>

<h:body>
<h:form id="topform">
<h:panelGroup id="container">
<my:include src="/test.xhtml" />
</h:panelGroup>
</h:form>
</h:body>

这在 text.xhtml
<ui:repeat value="#{some.values}" var="val">
<h:commandLink value="#{val}" action="#{some.setInclude(val)}">
<f:ajax render=":topform:container" />
</h:commandLink>
</ui:repeat>

这足以产生这样的错误:
javax.faces.FacesException: Cannot add the same component twice: topform:j_id-549384541_7e08d92c

最佳答案

对于 OmniFaces ,我也曾经通过创建一个 <o:include> 来试验这个如 UIComponent 而不是 TagHandler 这是一个 FaceletContext#includeFacelet() encodeChildren() 方法。通过这种方式,在恢复 View 阶段会记住正确包含的 facelet,并且包含的​​组件树仅在渲染响应阶段发生变化,这正是我们想要实现的构造。

这是一个基本的开球示例:

@FacesComponent("com.example.Include")
public class Include extends UIComponentBase {

@Override
public String getFamily() {
return "com.example.Include";
}

@Override
public boolean getRendersChildren() {
return true;
}

@Override
public void encodeChildren(FacesContext context) throws IOException {
getChildren().clear();
((FaceletContext) context.getAttributes().get(FaceletContext.FACELET_CONTEXT_KEY)).includeFacelet(this, getSrc());
super.encodeChildren(context);
}

public String getSrc() {
return (String) getStateHelper().eval("src");
}

public void setSrc(String src) {
getStateHelper().put("src", src);
}

}

注册于 .taglib.xml如下:
<tag>
<tag-name>include</tag-name>
<component>
<component-type>com.example.Include</component-type>
</component>
<attribute>
<name>src</name>
<required>true</required>
<type>java.lang.String</type>
</attribute>
</tag>

这适用于以下 View :
<h:outputScript name="fixViewState.js" />

<h:form>
<ui:repeat value="#{includeBean.includes}" var="include">
<h:commandButton value="Include #{include}" action="#{includeBean.setInclude(include)}">
<f:ajax render=":include" />
</h:commandButton>
</ui:repeat>
</h:form>

<h:panelGroup id="include">
<my:include src="#{includeBean.include}.xhtml" />
</h:panelGroup>

以及以下支持 bean:
@ManagedBean
@ViewScoped
public class IncludeBean implements Serializable {

private List<String> includes = Arrays.asList("include1", "include2", "include3");
private String include = includes.get(0);

private List<String> getIncludes() {
return includes;
}

public void setInclude(String include) {
return this.include = include;
}

public String getInclude() {
return include;
}

}

(此示例期望包含文件 include1.xhtmlinclude2.xhtmlinclude3.xhtml 与主文件位于同一基本文件夹中)
fixViewState.js可以在这个答案中找到: h:commandButton/h:commandLink does not work on first click, works only on second click .为了修复 JSF issue 790 这个脚本是强制性的当有多个 ajax 表单更新彼此的父表单时, View 状态会丢失。

还要注意,这样每个包含文件都可以有自己的 <h:form>必要时,因此您不一定需要将其放在 include 周围。

这种方法在 Mojarra 中工作正常,即使回发请求来自包含内部的表单,但它在 MyFaces 中很难失败,在初始请求期间已经出现以下异常:
java.lang.NullPointerException
at org.apache.myfaces.view.facelets.impl.FaceletCompositionContextImpl.generateUniqueId(FaceletCompositionContextImpl.java:910)
at org.apache.myfaces.view.facelets.impl.DefaultFaceletContext.generateUniqueId(DefaultFaceletContext.java:321)
at org.apache.myfaces.view.facelets.compiler.UIInstructionHandler.apply(UIInstructionHandler.java:87)
at javax.faces.view.facelets.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:49)
at org.apache.myfaces.view.facelets.tag.ui.CompositionHandler.apply(CompositionHandler.java:158)
at org.apache.myfaces.view.facelets.compiler.NamespaceHandler.apply(NamespaceHandler.java:57)
at org.apache.myfaces.view.facelets.compiler.EncodingHandler.apply(EncodingHandler.java:48)
at org.apache.myfaces.view.facelets.impl.DefaultFacelet.include(DefaultFacelet.java:394)
at org.apache.myfaces.view.facelets.impl.DefaultFacelet.include(DefaultFacelet.java:448)
at org.apache.myfaces.view.facelets.impl.DefaultFacelet.include(DefaultFacelet.java:426)
at org.apache.myfaces.view.facelets.impl.DefaultFaceletContext.includeFacelet(DefaultFaceletContext.java:244)
at com.example.Include.encodeChildren(Include.java:54)

MyFaces 基本上在 View 构建时间结束时释放 Facelet 上下文,使其在 View 呈现时间期间不可用,从而导致 NPE,因为内部状态有几个无效的属性。但是,可以在渲染期间添加单个组件而不是 Facelet 文件。我真的没有时间去调查这是我的错还是 MyFaces 的错。这也是为什么它还没有出现在 OmniFaces 中的原因。

如果您无论如何都在使用 Mojarra,请随意使用它。但是,我强烈建议在同一页面上使用所有可能的用例对其进行彻底测试。 Mojarra 有一些与状态保存相关的怪癖,在使用此构造时可能会失败。

关于ajax - 带有 <ui :include> 的动态 ajax 导航,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13680192/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com