自定义标记属性正在泄漏到子项中
Custom tag attributes are leaking into children
我们的应用程序使用的是 Mojarra 2.1.29-03,我们的自定义标签中的属性也有问题,因为它们也被复制到嵌套标签中。
例如,给定以下标记定义:
cc.taglib.xml
<?xml version="1.0" encoding="UTF-8" ?>
<facelet-taglib version="2.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facelettaglibrary_2_0.xsd">
<namespace>http://temp.com/jsf/customcomponents</namespace>
<tag>
<tag-name>wrapper</tag-name>
<source>wrapper.xhtml</source>
<attribute>
<description>The style class for the component</description>
<name>styleClass</name>
</attribute>
</tag>
</facelet-taglib>
wrapper.xhtml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<ui:component xmlns:ui="http://java.sun.com/jsf/facelets">
<div style="border: 1px solid black; margin: 5px;">
<p>styleClass: #{styleClass}</p>
<ui:insert />
</div>
</ui:component>
</html>
还有一个客户页面:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:cc="http://temp.com/jsf/customcomponents">
<h:body>
<cc:wrapper styleClass="cc-style">
<cc:wrapper />
</cc:wrapper>
</h:body>
</html>
结果如下:
styleClass: cc-style
styleClass: cc-style
...所以 styleClass 属性也被应用到内部标签,即使客户端页面没有设置它。
我们注意到,如果未明确设置,我们可以通过处理所有客户端页面来设置 styleClass="" 来解决此问题,但这是我们希望避免的方法(这是一个非常丑陋的解决方案,不能强制前进)。
这是一个错误吗?除了上述方法之外,还有其他解决方法吗 - 最好是在代码而不是客户端页面中使用解决方法?
谢谢
艾弗
这不是严格意义上的错误,但这确实是不符合直觉和不需要的行为,应该在 JSF/Facelets 规范中解决。
变通解决方案并不简单,需要自定义标签处理程序来清除 Facelet 范围。 JSF 实用程序库 OmniFaces has since version 2.1 exactly such one: <o:tagAttribute>
(source code here).
用法(注意 prolog、doctype 和 html 标签是不必要的,这是完整的文件):
<ui:composition
xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:o="http://omnifaces.org/ui"
>
<o:tagAttribute name="styleClass" />
<div style="border: 1px solid black; margin: 5px;">
<p>styleClass: #{styleClass}</p>
<ui:insert />
</div>
</ui:composition>
与具体问题无关,这不是自定义组件,而是标签文件。而且,在 JSF 2.x 中,术语 "cc" 通常与复合组件相关联,这是一个完全不同的主题。要正确使用您的知识和术语(这样您在阅读您或他人的代码时就不会混淆自己或他人),请前往 When to use <ui:include>, tag files, composite components and/or custom components?
关于信息,我们确定了一种可能对其他人有用的替代方法,因为它更容易实施和理解。
首先,我需要为我们发现自己所处的情况添加一些上下文......我们最近从 2.1.12 迁移到 2.1.29-03,作为该过程的一部分,我们更改了我们的一个从复合组件(其属性具有隔离范围)到自定义标签(具有上述行为)的构建块。 2.1.12 中存在关于自定义标签属性的相同行为,但我们没有意识到这一点,因此我们考虑了以下方法,因为它会恢复我们在迁移之前的行为,因为我们只需要将它应用于我们更改的组件.
wrapper.xhtml(我已将命名空间更改为 'ct' 而不是 'cc')
<ui:component xmlns:ct="http://temp.com/jsf/customtags" xmlns:ui="http://java.sun.com/jsf/facelets">
<ct:tagAttribute name="styleClass" />
<div style="border: 1px solid black; margin: 5px;">
<p>styleClass: #{styleClass}</p>
<ct:eraseAttribute name="styleClass" />
<ui:insert />
</div>
</ui:component>
其中 tagAttribute 是 BalusC 建议的解决方案,以防止标签从其父项继承属性,此外我们在调用 ui:insert 之前调用 eraseAttribute 标签以从范围中删除属性(这显然需要自定义标签以属性结束)。这意味着这个标签既不继承也不泄漏属性,其他标签可以保持不变并保持迁移前的相同行为。
ct.taglib.xhtml(片段)
<tag>
<tag-name>eraseAttribute</tag-name>
<handler-class>com.temp.jsf.customtags.EraseTagAttribute</handler-class>
<attribute>
<name>name</name>
<required>true</required>
<type>java.lang.String</type>
</attribute>
</tag>
EraseTagAttribute.java
package com.temp.jsf.customtags;
import java.io.IOException;
import javax.faces.component.UIComponent;
import javax.faces.view.facelets.FaceletContext;
import javax.faces.view.facelets.TagConfig;
import javax.faces.view.facelets.TagHandler;
public class EraseTagAttribute extends TagHandler {
private final String name;
public EraseTagAttribute(TagConfig config) {
super(config);
name = getRequiredAttribute("name").getValue();
}
public void apply(FaceletContext context, UIComponent parent) throws IOException {
context.getVariableMapper().setVariable(name, null);
}
}
最终我们没有使用它,因为我们认为 BalusC 提供的答案(我们将其应用于我们所有自定义标签中的每个属性)是正确且更清晰的方法,即使它可能对我们非常具体的情况产生额外的影响情况。
我们的应用程序使用的是 Mojarra 2.1.29-03,我们的自定义标签中的属性也有问题,因为它们也被复制到嵌套标签中。
例如,给定以下标记定义:
cc.taglib.xml
<?xml version="1.0" encoding="UTF-8" ?>
<facelet-taglib version="2.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facelettaglibrary_2_0.xsd">
<namespace>http://temp.com/jsf/customcomponents</namespace>
<tag>
<tag-name>wrapper</tag-name>
<source>wrapper.xhtml</source>
<attribute>
<description>The style class for the component</description>
<name>styleClass</name>
</attribute>
</tag>
</facelet-taglib>
wrapper.xhtml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<ui:component xmlns:ui="http://java.sun.com/jsf/facelets">
<div style="border: 1px solid black; margin: 5px;">
<p>styleClass: #{styleClass}</p>
<ui:insert />
</div>
</ui:component>
</html>
还有一个客户页面:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:cc="http://temp.com/jsf/customcomponents">
<h:body>
<cc:wrapper styleClass="cc-style">
<cc:wrapper />
</cc:wrapper>
</h:body>
</html>
结果如下:
styleClass: cc-style
styleClass: cc-style
...所以 styleClass 属性也被应用到内部标签,即使客户端页面没有设置它。
我们注意到,如果未明确设置,我们可以通过处理所有客户端页面来设置 styleClass="" 来解决此问题,但这是我们希望避免的方法(这是一个非常丑陋的解决方案,不能强制前进)。
这是一个错误吗?除了上述方法之外,还有其他解决方法吗 - 最好是在代码而不是客户端页面中使用解决方法?
谢谢 艾弗
这不是严格意义上的错误,但这确实是不符合直觉和不需要的行为,应该在 JSF/Facelets 规范中解决。
变通解决方案并不简单,需要自定义标签处理程序来清除 Facelet 范围。 JSF 实用程序库 OmniFaces has since version 2.1 exactly such one: <o:tagAttribute>
(source code here).
用法(注意 prolog、doctype 和 html 标签是不必要的,这是完整的文件):
<ui:composition
xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:o="http://omnifaces.org/ui"
>
<o:tagAttribute name="styleClass" />
<div style="border: 1px solid black; margin: 5px;">
<p>styleClass: #{styleClass}</p>
<ui:insert />
</div>
</ui:composition>
与具体问题无关,这不是自定义组件,而是标签文件。而且,在 JSF 2.x 中,术语 "cc" 通常与复合组件相关联,这是一个完全不同的主题。要正确使用您的知识和术语(这样您在阅读您或他人的代码时就不会混淆自己或他人),请前往 When to use <ui:include>, tag files, composite components and/or custom components?
关于信息,我们确定了一种可能对其他人有用的替代方法,因为它更容易实施和理解。
首先,我需要为我们发现自己所处的情况添加一些上下文......我们最近从 2.1.12 迁移到 2.1.29-03,作为该过程的一部分,我们更改了我们的一个从复合组件(其属性具有隔离范围)到自定义标签(具有上述行为)的构建块。 2.1.12 中存在关于自定义标签属性的相同行为,但我们没有意识到这一点,因此我们考虑了以下方法,因为它会恢复我们在迁移之前的行为,因为我们只需要将它应用于我们更改的组件.
wrapper.xhtml(我已将命名空间更改为 'ct' 而不是 'cc')
<ui:component xmlns:ct="http://temp.com/jsf/customtags" xmlns:ui="http://java.sun.com/jsf/facelets">
<ct:tagAttribute name="styleClass" />
<div style="border: 1px solid black; margin: 5px;">
<p>styleClass: #{styleClass}</p>
<ct:eraseAttribute name="styleClass" />
<ui:insert />
</div>
</ui:component>
其中 tagAttribute 是 BalusC 建议的解决方案,以防止标签从其父项继承属性,此外我们在调用 ui:insert 之前调用 eraseAttribute 标签以从范围中删除属性(这显然需要自定义标签以属性结束)。这意味着这个标签既不继承也不泄漏属性,其他标签可以保持不变并保持迁移前的相同行为。
ct.taglib.xhtml(片段)
<tag>
<tag-name>eraseAttribute</tag-name>
<handler-class>com.temp.jsf.customtags.EraseTagAttribute</handler-class>
<attribute>
<name>name</name>
<required>true</required>
<type>java.lang.String</type>
</attribute>
</tag>
EraseTagAttribute.java
package com.temp.jsf.customtags;
import java.io.IOException;
import javax.faces.component.UIComponent;
import javax.faces.view.facelets.FaceletContext;
import javax.faces.view.facelets.TagConfig;
import javax.faces.view.facelets.TagHandler;
public class EraseTagAttribute extends TagHandler {
private final String name;
public EraseTagAttribute(TagConfig config) {
super(config);
name = getRequiredAttribute("name").getValue();
}
public void apply(FaceletContext context, UIComponent parent) throws IOException {
context.getVariableMapper().setVariable(name, null);
}
}
最终我们没有使用它,因为我们认为 BalusC 提供的答案(我们将其应用于我们所有自定义标签中的每个属性)是正确且更清晰的方法,即使它可能对我们非常具体的情况产生额外的影响情况。