自定义标记属性泄露给孩子

时间:2015-07-13 12:04:48

标签: jsf jsf-2 facelets mojarra tagfile

我们的应用程序使用的是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 =&#34;&#34;如果没有明确设置,但这是我们想要避免的方法(它是一个非常丑陋的解决方案,不能在未来强制实施)。

这是一个错误吗?有没有办法解决上面提到的问题 - 最好是使用标记而不是客户端页面中的解决方法?

由于 艾弗

2 个答案:

答案 0 :(得分:2)

这不是严格意义上的错误,但这确实是不直观和不受欢迎的行为,应该在JSF / Facelets规范中解决。

解决方案的解决方案并非易事,需要自定义标记处理程序来清除Facelet范围。自版本2.1以来,JSF实用程序库OmniFaces就是这样一个:<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中,术语&#34; cc&#34;通常与复合组件相关联,这是完全不同的主题。为了使您的知识和术语正确(这样您在阅读或其他代码时不会让自己或他人感到困惑),请前往When to use <ui:include>, tag files, composite components and/or custom components?

答案 1 :(得分:0)

有关信息,我们确定了一种可能对其他人有用的替代方法,因为它更容易实现和理解。

首先,我需要为我们发现自己的情况添加一些背景...我们最近从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之前调用eraseAttribute标记:insert以从范围中删除属性(这显然要求自定义标记已完成属性)。这意味着这一个标记既不继承也不泄漏属性,其他标记可以保持不变并保持迁移前的相同行为。

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(我们应用于所有自定义标签中的每个属性)提供的答案是正确和更清洁的方法,即使它可能在我们非常具体的情况下产生额外的后果。