2
votes

I'm trying to implement lazy loading in an ace:dataTable. My web application has quite a lot of tables, so I've tried to reduce redundancy by using templates for the columns. Currently my tables look like this:

Datatable in my xhtml page

<ace:dataTable 
    id="produktdatenTabelle"
        value="#{produktdatenBean.lazyModel}"
        var="row"
        rows="20"
        paginator="true"
        paginatorPosition="bottom"
        paginatorAlwaysVisible="true"
        lazy="true">

    <ui:include src="/resources/aceDataTable/column.xhtml">
        <ui:param name="title" value="ID" />
        <ui:param name="value" value="#{row.id}" />
    </ui:include>

    <ui:include src="/resources/aceDataTable/column.xhtml">
        <ui:param name="title" value="Description" />
        <ui:param name="value" value="#{row.description}" />
    </ui:include>

</ace:dataTable>

column.xhtml

<ui:composition>
    <ace:column headerText="#{title}" sortBy="#{value}" filterBy="#{value}" filterMatchMode="contains">
        <c:choose>
            <!-- Editable -->
            <c:when test="${editable == 'true'}">
                <ace:cellEditor>
                    <f:facet name="output">
                        <h:outputText value="#{value}"/>
                    </f:facet>
                    <f:facet name="input">
                        <h:inputText value="#{value}" />
                    </f:facet>
                </ace:cellEditor>
            </c:when>
            <!-- Not editable -->
            <c:otherwise>
                <h:outputText value="#{value}"/>
            </c:otherwise>
        </c:choose>
    </ace:column>
</ui:composition>

Class of produktdatenBean.lazyModel

public class LazyDataModelImpl<D> extends LazyDataModel<D>
{

    @Override
    public List<D> load(int first, int pageSize, SortCriteria[] sortCriteria, Map<String, String> filters)
    {
        ...
    }

}

The parameters 'first' and 'pageSize' are passed correctly and I can use them, to load my object from the database. So everything working there. But now I'm trying to sort.

If I sort by the column ID, I get an object of SortCriteria in the array 'sortCriteria' (as expected). Unfortunately it has set its propertyName to '#{value' instead of 'id'. So parameters inside the template don't get resolved when passed to the load() method.

If remove the templates and change my table to

Datatable with templates removed

<ace:dataTable 
        id="produktdatenTabelle"
        value="#{produktdatenBean.lazyModel}"
        var="row"
        rows="20"
        paginator="true"
        paginatorPosition="bottom"
        paginatorAlwaysVisible="true"
        lazy="true">

    <ace:column headerText="ID" sortBy="#{row.id}" filterBy="#{row.id}" filterMatchMode="contains">
        <h:outputText value="#{row.id}"/>
    </ace:column>

    <ace:column headerText="Description" sortBy="#{row.description}" filterBy="#{row.description}" filterMatchMode="contains">
        <h:outputText value="#{row.description}"/>
    </ace:column>

</ace:dataTable>

everything works as expected (the SortCriteria has set propertyName to 'id').

So my question is: Can I use templates with a lazy loading ace:dataTable or is this not supposed to work? If it is possible, what do I have to do to get the parameters passed correctly?

1
can you please share with us the lazyModel class implementation ?Mahmoud Saleh
Off topic but IceFaces is a fork of PrimeFaces, in case you don't know. blog.primefaces.org/?p=1750Cagatay Civici

1 Answers

0
votes

You need a Facelets tag file instead of a Facelets include file.

The include file approach basically builds the include file only once during view build time and inserts the resulting JSF component tree in the place, which in turn get processed during view render time. Because JSTL tags are executed during view build time, this means that all JSTL tags are already processed at that point. However, the JSF datatable isn't processed during view build time, but only during view render time, so its var would always evaluate to null and hence the #{value} is always null in the include file.

The tag file approach will process the tag file during view render time, so you're basically moving the JSTL job from view build time to view render time and then the var of the datatable will be available in the EL scope.

You can keep the column.xhtml code untouched (I'd only put it in /WEB-INF so that endusers won't be able to request it standalone). You only need to create a .taglib.xml file, e.g. /WEB-INF/custom.taglib.xml:

<?xml version="1.0" encoding="UTF-8"?>
<facelet-taglib 
    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"
    version="2.0"
>
    <namespace>http://example.com/custom</namespace>

    <tag>
        <tag-name>column</tag-name>
        <source>/WEB-INF/tags/column.xhtml</source>
    </tag>
</facelet-taglib>

If you register this as follows in /WEB-INF/web.xml,

<context-param>
    <param-name>javax.faces.FACELETS_LIBRARIES</param-name>
    <param-value>/WEB-INF/custom.taglib.xml</param-value>
</context-param>

(the param value can refer multiple taglibs, you just need to semicolon-separate them)

Then you'll be able to use it as follows,

<html ... xmlns:my="http://example.com/custom">

...

<ace:dataTable 
    id="produktdatenTabelle"
        value="#{produktdatenBean.lazyModel}"
        var="row"
        rows="20"
        paginator="true"
        paginatorPosition="bottom"
        paginatorAlwaysVisible="true"
        lazy="true">

    <my:column title="ID" value="#{row.id}" />
    <my:column title="Description" value="#{row.description}" />
</ace:dataTable>

See also: