使用 JSF Datatable 有条件地显示行

Posted

技术标签:

【中文标题】使用 JSF Datatable 有条件地显示行【英文标题】:Conditionally display row using JSF Datatable 【发布时间】:2011-02-19 05:50:39 【问题描述】:

我有一些当前可以工作的 JSF 代码(如下所示),我需要对其进行修改以有条件地禁止显示表格的某些行。我知道如何有条件地抑制特定单元格的显示,但这似乎会创建一个空单元格,而我想要做的是根本不显示该行。

有什么建议吗?

<h:dataTable styleClass="resultsTable" id="t1" value="#r.common" var="com" headerClass="headerBackgrnd" rowClasses="rowOdd, rowEven" columnClasses="leftAlign, rightAlign, leftAlign">
    <h:column>
        <h:outputText rendered="#com.rendered" styleClass="inputText" value="#com.description: " />
    </h:column>
    <h:column>
        <h:outputText styleClass="outputText" value="#com.v1" />
    </h:column>
    <h:column>
        <h:inputText styleClass="inputText" value="#com.v2" />
   </h:column>
</h:dataTable>

基本上,#com.rendered 的行将有条件地显示单个单元格的内容,当 com.rendered 为 false 时会生成一个空单元格。但我想在某些情况下跳过整行显示 - 我该怎么做?

【问题讨论】:

【参考方案1】:

通过在所有&lt;h:column&gt; 标签中放置一个渲染属性,我成功地隐藏了行。问题是它抑制了表格标题。如果您的表格没有表格标题(它们是嵌入在&lt;h:column&gt; 中的&lt;f:facet name="header"&gt; 标签),这种方法可能适合您。

我最终在支持 bean 中使用了多个列表,因为我需要表头。

【讨论】:

试过这个,除非我做错了所有列都呈现解析为FALSE的行,仍然呈现为空行(即&lt;tr&gt;&lt;/tr&gt;【参考方案2】:

使用此处建议的空 css 选择器,但使用 tr 而不是 td。这对我有用。

https://***.com/a/19177424

如此处所述:https://developer.mozilla.org/en-US/docs/Web/CSS/:empty 此选择器适用于当前所有浏览器。

<style>
  tr:empty 
    display: none;
  
</style>

【讨论】:

【参考方案3】:

我扩展 HtmlTableRenderer 默认渲染器并覆盖 renderRowStart 方法,通过将 style 属性赋予 table- >tr 元素,值为 display:none

绑定列表中的item需要实现TableRow接口,只有一个isHide方法。在具体类中,您可以放置​​您喜欢的任何逻辑来提供布尔值。

顺便说一句,在这个自定义渲染器中也有类似 PrimeFaces 实现的函数,它在表为空时给出消息,并且 table->tr 将自动计算表中有多少列并为 colspan 属性提供适当的值。

public class MyDataTableRenderer extends htmlTableRenderer 
    private static final Integer[] ZERO_INT_ARRAY = new Integer[]  0 ;
    private static final String NO_RESULT_MESSAGE_ATTR_NAME = "noResultMessage";
    private static final String defaultEmptyMessage = "No records found";
    private static final Logger log = Logger.getLogger(DHSDataTableRenderer.class.getName());

@Override
public void encodeInnerHtml(FacesContext facesContext, UIComponent component) throws IOException 
    UIData uiData = (UIData) component;
    String message = (String) uiData.getAttributes().get(NO_RESULT_MESSAGE_ATTR_NAME);
    if (message == null || "".equals(message.trim())) 
        message = defaultEmptyMessage;
    

    ResponseWriter writer = facesContext.getResponseWriter();

    int rowCount = uiData.getRowCount();

    int newspaperColumns = getNewspaperColumns(component);

    int columnNumber = getChildCount(component);

    if (rowCount == -1 && newspaperColumns == 1) 
        encodeInnerHtmlUnknownRowCount(facesContext, component);
        return;
    

    if (rowCount == 0) 
        // nothing to render, to get valid xhtml we render an empty dummy
        // row
        writer.startElement(HTML.TBODY_ELEM, uiData);
        writer.writeAttribute(HTML.ID_ATTR, component.getClientId(facesContext) + ":tbody_element", null);
        writer.startElement(HTML.TR_ELEM, uiData);
        writer.startElement(HTML.TD_ELEM, uiData);
        writer.writeAttribute(HTML.COLSPAN_ATTR, columnNumber, null);
        writer.writeAttribute(HTML.CLASS_ATTR, "dhs-empty-table", null);
        writer.write(message);
        writer.endElement(HTML.TD_ELEM);
        writer.endElement(HTML.TR_ELEM);
        writer.endElement(HTML.TBODY_ELEM);
        return;
    

    // begin the table
    // get the CSS styles
    Styles styles = getStyles(uiData);

    int first = uiData.getFirst();
    int rows = uiData.getRows();
    int last;

    if (rows <= 0) 
        last = rowCount;
     else 
        last = first + rows;
        if (last > rowCount) 
            last = rowCount;
        
    

    int newspaperRows;
    if ((last - first) % newspaperColumns == 0) 
        newspaperRows = (last - first) / newspaperColumns;
     else 
        newspaperRows = ((last - first) / newspaperColumns) + 1;
    
    boolean newspaperHorizontalOrientation = isNewspaperHorizontalOrientation(component);

    // get the row indizes for which a new TBODY element should be created
    Integer[] bodyrows = getBodyRows(facesContext, component);
    int bodyrowsCount = 0;

    // walk through the newspaper rows
    for (int nr = 0; nr < newspaperRows; nr++) 
        boolean rowStartRendered = false;
        // walk through the newspaper columns
        for (int nc = 0; nc < newspaperColumns; nc++) 

            // the current row in the 'real' table
            int currentRow;
            if (newspaperHorizontalOrientation) 
                currentRow = nr * newspaperColumns + nc + first;
             else 
                currentRow = nc * newspaperRows + nr + first;
            

            // if this row is not to be rendered
            if (currentRow >= last) 
                continue;
            

            // bail if any row does not exist
            uiData.setRowIndex(currentRow);
            if (!uiData.isRowAvailable()) 
                log.severe("Row is not available. Rowindex = " + currentRow);
                break;
            

            if (nc == 0) 
                // first column in table, start new row
                beforeRow(facesContext, uiData);

                // is the current row listed in the bodyrows attribute
                if (ArrayUtils.contains(bodyrows, currentRow)) 
                    // close any preopened TBODY element first
                    if (bodyrowsCount != 0) 
                        HtmlRendererUtils.writePrettyLineSeparator(facesContext);
                        writer.endElement(HTML.TBODY_ELEM);
                    
                    HtmlRendererUtils.writePrettyLineSeparator(facesContext);
                    writer.startElement(HTML.TBODY_ELEM, uiData);
                    // Do not attach bodyrowsCount to the first TBODY
                    // element, because of backward compatibility
                    writer.writeAttribute(HTML.ID_ATTR, component.getClientId(facesContext) + ":tbody_element" + (bodyrowsCount == 0 ? "" : bodyrowsCount),
                            null);
                    bodyrowsCount++;
                

                HtmlRendererUtils.writePrettyLineSeparator(facesContext);
                renderRowStart(facesContext, writer, uiData, styles, nr);
                rowStartRendered = true;
            

            List<UIComponent> children = null;
            for (int j = 0, size = getChildCount(component); j < size; j++) 
                if (children == null) 
                    children = getChildren(component);
                
                UIComponent child = children.get(j);
                if (child.isRendered()) 
                    boolean columnRendering = child instanceof UIColumn;

                    if (columnRendering) 
                        beforeColumn(facesContext, uiData, j);
                    

                    encodeColumnChild(facesContext, writer, uiData, child, styles, nc * uiData.getChildCount() + j);

                    if (columnRendering) 
                        afterColumn(facesContext, uiData, j);
                    
                
            

            if (hasNewspaperTableSpacer(uiData)) 
                // draw the spacer facet
                if (nc < newspaperColumns - 1) 
                    renderSpacerCell(facesContext, writer, uiData);
                
            
        
        if (rowStartRendered) 
            renderRowEnd(facesContext, writer, uiData);
            afterRow(facesContext, uiData);
        
    

    if (bodyrowsCount != 0) 
        // close the last TBODY element
        HtmlRendererUtils.writePrettyLineSeparator(facesContext);
        writer.endElement(HTML.TBODY_ELEM);
    


@Override
protected void renderRowStart(FacesContext facesContext, ResponseWriter writer, UIData uiData, Styles styles, int rowStyleIndex) throws IOException 
    writer.startElement(HTML.TR_ELEM, null); // uiData);

    renderRowStyle(facesContext, writer, uiData, styles, rowStyleIndex);
    Object obj = uiData.getRowData();
    boolean isHide = false;
    if (obj instanceof TableRow) 
        isHide = ((TableRow) obj).isHide();
    
    if (isHide) 
        writer.writeAttribute("style", "display: none;", null);
    
    Object rowId = uiData.getAttributes().get(org.apache.myfaces.shared.renderkit.JSFAttr.ROW_ID);

    if (rowId != null) 
        writer.writeAttribute(HTML.ID_ATTR, rowId.toString(), null);
    


private void encodeInnerHtmlUnknownRowCount(FacesContext facesContext, UIComponent component) throws IOException 
    UIData uiData = (UIData) component;
    ResponseWriter writer = facesContext.getResponseWriter();

    Styles styles = getStyles(uiData);

    Integer[] bodyrows = getBodyRows(facesContext, component);
    int bodyrowsCount = 0;

    int first = uiData.getFirst();
    int rows = uiData.getRows();
    int currentRow = first;
    boolean isRowRendered = false;

    while (true) 
        uiData.setRowIndex(currentRow);
        if (!uiData.isRowAvailable()) 
            break;
        

        isRowRendered = true;

        // first column in table, start new row
        beforeRow(facesContext, uiData);

        // is the current row listed in the bodyrows attribute
        if (ArrayUtils.contains(bodyrows, currentRow)) 
            // close any preopened TBODY element first
            if (bodyrowsCount != 0) 
                HtmlRendererUtils.writePrettyLineSeparator(facesContext);
                writer.endElement(HTML.TBODY_ELEM);
            
            HtmlRendererUtils.writePrettyLineSeparator(facesContext);
            writer.startElement(HTML.TBODY_ELEM, uiData);
            // Do not attach bodyrowsCount to the first TBODY element,
            // because of backward compatibility
            writer.writeAttribute(HTML.ID_ATTR, component.getClientId(facesContext) + ":tbody_element" + (bodyrowsCount == 0 ? "" : bodyrowsCount), null);
            bodyrowsCount++;
        

        HtmlRendererUtils.writePrettyLineSeparator(facesContext);
        renderRowStart(facesContext, writer, uiData, styles, currentRow);

        List<UIComponent> children = null;
        for (int j = 0, size = getChildCount(component); j < size; j++) 
            if (children == null) 
                children = getChildren(component);
            
            UIComponent child = children.get(j);
            if (child.isRendered()) 
                boolean columnRendering = child instanceof UIColumn;

                if (columnRendering) 
                    beforeColumn(facesContext, uiData, j);
                

                encodeColumnChild(facesContext, writer, uiData, child, styles, j);

                if (columnRendering) 
                    afterColumn(facesContext, uiData, j);
                
            
        

        renderRowEnd(facesContext, writer, uiData);
        afterRow(facesContext, uiData);

        currentRow++;

        if (rows > 0 && currentRow - first > rows) 
            break;
        
    

    if (!isRowRendered) 
        // nothing to render, to get valid xhtml we render an empty dummy
        // row
        writer.startElement(HTML.TBODY_ELEM, uiData);
        writer.writeAttribute(HTML.ID_ATTR, component.getClientId(facesContext) + ":tbody_element", null);
        writer.startElement(HTML.TR_ELEM, uiData);
        writer.startElement(HTML.TD_ELEM, uiData);
        writer.endElement(HTML.TD_ELEM);
        writer.endElement(HTML.TR_ELEM);
        writer.endElement(HTML.TBODY_ELEM);
        return;
    

    if (bodyrowsCount != 0) 
        // close the last TBODY element
        HtmlRendererUtils.writePrettyLineSeparator(facesContext);
        writer.endElement(HTML.TBODY_ELEM);
    


private Integer[] getBodyRows(FacesContext facesContext, UIComponent component) 
    Integer[] bodyrows = null;
    String bodyrowsAttr = (String) component.getAttributes().get(JSFAttr.BODYROWS_ATTR);
    if (bodyrowsAttr != null && !"".equals(bodyrowsAttr)) 
        String[] bodyrowsString = StringUtils.trim(StringUtils.splitShortString(bodyrowsAttr, ','));
        // parsing with no exception handling, because of JSF-spec:
        // "If present, this must be a comma separated list of integers."
        bodyrows = new Integer[bodyrowsString.length];
        for (int i = 0; i < bodyrowsString.length; i++) 
            bodyrows[i] = new Integer(bodyrowsString[i]);
        

     else 
        bodyrows = ZERO_INT_ARRAY;
    
    return bodyrows;

【讨论】:

在我之前的cmets中忘记提及了。我们需要将以下新元素添加到 faces-config.xml &lt;render-kit&gt; &lt;renderer&gt; &lt;component-family&gt;javax.faces.Data&lt;/component-family&gt; &lt;renderer-type&gt;javax.faces.Table&lt;/renderer-type&gt; &lt;renderer-class&gt;xxx.xxx.MyDataTableRenderer &lt;/renderer-class&gt; &lt;/renderer&gt; &lt;/render-kit&gt; @Laila Agaev【参考方案4】:

Brian 解决方案的扩展。 为了显示列名,我在 primefaces 中做了以下操作

<p:dataTable value="#eiBean.dce.ilDbConns" var="c">
    <p:columnGroup type="header">
        <p:row>
            <p:column colspan="1" />
            <p:column colspan="1" />
        </p:row>
        <p:row>
            <p:column headerText="DataBase Type"  />
            <p:column headerText="URL"  />
        </p:row>
    </p:columnGroup>
    <p:column rendered='#c.conType == "TARGET"'>
        <p:outputLabel value="#c.dbType" />
    </p:column>
    <p:column rendered='#c.conType == "TARGET"'>
        <p:outputLabel value="#c.dbUrl" />
    </p:column>         
</p:dataTable>

【讨论】:

【参考方案5】:

对于使用richFaces 的人,您可以使用rich:column 的filterExpression 属性。

<rich:column filterExpression="#put your expression here">
    ...
</rich>

如果不满足条件,则过滤掉整行。

示例是使用接缝 EL!

【讨论】:

我不明白这是如何工作的,您对列过滤器表达式的设置但行消失了吗?【参考方案6】:

行对应于表集合中的数据对象。如果您不想要该行,请不要将对象放入集合中。

或者,您可以将rowClasses 参数用于dataTable。

豆码:

public String getRowClasses() 
    StringBuilder sb = new StringBuilder();
    for (Data data : myData) 
        sb.append(data.hide ? 'hide,' : 'show,');
    
    return sb.toString();

CSS:

tr.hide display:none;

【讨论】:

抱歉,这不是一个真正的选项 - 此表显示某些应用程序范围的参考数据,除非在此特定情况下,数据只能在某些条件下显示。 那么两个集合,一个包含所有数据,第二个包含用于显示的内容? 问题是我有 2 行,根据情况,有时会显示 A 行,有时会显示 B 行,有时会同时显示两者。保留多套意味着我必须保留三个版本,这并不理想。我希望有另一种方法可以做到这一点。 它仍然只有 2 个集合,您只需根据需要更改显示集合的内容。但是,如果您不介意在您的 M 中添加一个小 V,则可以通过单个集合以编程方式执行此操作。上面已编辑。 纳加纳尔夫是对的。在 bean 级别过滤它。遍历主要的List 并仅收集另一个List 中感兴趣的条目。它不应该那么贵,因为 List 只包含引用。

以上是关于使用 JSF Datatable 有条件地显示行的主要内容,如果未能解决你的问题,请参考以下文章

JSF 命令按钮

如何有条件地设置丰富的行:dataTable

如何在没有 JSF 标记库(h:datatable 或 ui:repeat)的情况下编写 <table> 标记,但仍使用 JSF 来控制页面流

JSF 1.1:有条件地隐藏原始 HTML 和标签

JSF 对话框立即消失

JSTL c:forEach 在 JSF h:dataTable 中不起作用