JSF 2.0必需的字段标签装饰器,用于具有@NotNull约束的属性

时间:2013-02-08 19:44:07

标签: jsf-2 primefaces

我有一个 JSF 2.0应用程序,它也使用 Primefaces 3.3 。目前有一个很好的功能,如果相关的<p:inputText>使用required="true"属性,标签将用星号装饰。

此字段绑定到bean属性,该属性使用@NotNull验证约束进行注释。当bean属性已经使用@NotNull注释时,在XHTML中添加required="true"似乎是多余的并且容易出错。

是否有钩子或某种方法来自动装饰与@NotNull绑定到属性的组件的标签?

非常感谢任何想法或建议。

2 个答案:

答案 0 :(得分:3)

注意:这是一个黑客攻击。由于它使用了内省,它可能会对性能产生影响

  1. 在基本级别,如果字段使用@NotNull注释,您需要知道的内容。对于视图范围的bean,在@PostConstruct这样的合理位置执行此检查。声明一个全局变量以确定所需的属性

    boolean  requiredAttribute;        
    
    @PostConstruct
    public void init{ 
    Field theField = this.getClass().getField("theField");
    NotNull theAnnotation = theField.getAnnotation(NotNull.class);
    if(theAnnotation != null){
       requiredAttribute = true;
       }
    }
    
  2. required属性绑定到辅助bean中的变量

    <p:inputText id="inputit" required="#{myBean.requiredAttribute}"/>
    

答案 1 :(得分:3)

此解决方案基于PF 6.0,我不记得先前版本中是否有BeanValidationMetadataExtractor。无论如何,创建一个DIY提取器是一项简单的任务。

我有类似的问题。在我的具体案例中:

  • 应告知用户需要某个字段(阅读UIInput
  • 我不想在comp上重复required="true",因为它已经绑定到@NotNull / @NotBlank属性/字段
  • 在我的情况下,标签组件可能不存在(我不喜欢带星号的标签)

所以,这就是我所做的:

import java.util.Set;
import javax.el.ValueExpression;
import javax.faces.component.UIInput;
import javax.faces.context.FacesContext;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.PreRenderComponentEvent;
import javax.faces.event.SystemEvent;
import javax.faces.event.SystemEventListener;
import javax.validation.constraints.NotNull;
import javax.validation.metadata.ConstraintDescriptor;
import org.hibernate.validator.constraints.NotBlank;
import org.hibernate.validator.constraints.NotEmpty;
import org.omnifaces.util.Faces;
import org.primefaces.context.RequestContext;
import org.primefaces.metadata.BeanValidationMetadataExtractor;


public class InputValidatorConstraintListener implements SystemEventListener
{
    @Override
    public boolean isListenerForSource(Object source)
    {
        return source instanceof UIInput;
    }

    @Override
    public void processEvent(SystemEvent event) throws AbortProcessingException
    {
        if(event instanceof PreRenderComponentEvent)
        {
            UIInput component = (UIInput) event.getSource();

            component.getPassThroughAttributes().computeIfAbsent("data-required", k ->
            {
                ValueExpression requiredExpression = component.getValueExpression("required");
                if(requiredExpression != null || !component.isRequired())
                {
                    FacesContext context = Faces.getContext();
                    ValueExpression valueExpression = component.getValueExpression("value");
                    RequestContext requestContext = RequestContext.getCurrentInstance();

                    try
                    {
                        Set<ConstraintDescriptor<?>> constraints = BeanValidationMetadataExtractor.extractAllConstraintDescriptors(context, requestContext, valueExpression);
                        if(constraints != null && !constraints.isEmpty())
                        {
                            return constraints.stream()
                                .map(ConstraintDescriptor::getAnnotation)
                                .anyMatch(x -> x instanceof NotNull || x instanceof NotBlank || x instanceof NotEmpty);
                        }
                    }
                    catch(Exception e)
                    {
                        return false;
                    }
                }

                return false;
            });
        }
    }
}

并在faces-config.xml中声明它:

<?xml version="1.0" encoding="utf-8"?>
<faces-config version="2.2" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd">

    <application>
        <system-event-listener>
            <system-event-listener-class>it.shape.core.jsf.listener.InputValidatorConstraintListener</system-event-listener-class>
            <system-event-class>javax.faces.event.PreRenderComponentEvent</system-event-class>
        </system-event-listener>
    </application>

</faces-config>

通过此侦听器UIInput使用data-required passthrough属性进行渲染:

<input 
    id="form:editPanelMain:j_idt253" 
    name="form:editPanelMain:j_idt253" 
    type="text" 
    value="Rack Assemply" 
    size="80" 
    data-required="true"    <============================ NOTE THIS!!
    data-widget="widget_form_editPanelMain_j_idt253" 
    class="ui-inputfield ui-inputtext ui-widget ui-state-default ui-corner-all" 
    role="textbox" 
    aria-disabled="false" 
    aria-readonly="false">

现在,我使用css规则来突出显示这些字段:

input[data-required='true'], 
.ui-inputfield[data-required='true'], 
*[data-required='true']  .ui-inputfield {
    box-shadow: inset 0px 2px 2px #bf8f8f;
}

您可以调整此侦听器,以根据需要设置组件,或使用另一种方法满足您的特定需求。

另一种方法可能是:

  • 倾听UILabel而不是UIInput s
  • 获取与UIInput / for标签的ValueExpression相关联的forValue
  • 检查UIInput验证限制
  • 最终调用UIInput.setRequired(true)

性能影响可以忽略不计,因为我测试了大约3000个组件的复杂页面。