ICEfaces
  1. ICEfaces
  2. ICE-9948

ace:dataTable -use of el in filterBy and sortBy when lazy loading and dynamically altering columns

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: 3.3, EE-3.3.0.GA_P01
    • Fix Version/s: EE-3.3.0.GA_P02, 4.0
    • Component/s: ACE-Components
    • Labels:
      None
    • Environment:
      jsf 2 icefaces ace

      Description

      Currently, el expressions are not supported in lazy loading ace:dataTable when using dynamic ace:column and sortBy or filterBy. The el currently gets resolved as a String.

      See also http://jira.icesoft.org/browse/ICE-8378
      for more details as well as the older forum reference
      http://www.icesoft.org/JForum/posts/list/21598.page#sthash.YUID2JXd.dpbs
      http://www.icesoft.org/JForum/posts/list/22496.page

        Activity

        Hide
        Arturo Zambrano added a comment - - edited

        After developing a test case and trying to add this improvement, I realized that it's not possible to accomplish this in the way we intended.

        For lazy loading cases, we simply pass the property name itself to the load method, so that the app developer can identify which column was filtered and/or sorted, since we can't do the filtering and sorting from our component code since the entire data set is not in memory. Thus, we allow developers to proceed in any way they want.

        For example, if a column is defined like this:

        <ace:column headerText="Artist" sortBy="#{track.artist}" filterBy="#{track.artist}"/>
        

        We would pass the string 'artist' to the load() method of LazyDataModel as part of both the 'sortCriteria' and 'filters' objects. We do this by simply parsing the original expression string from the ValueExpression object and obtaining the property name. The problem described by this JIRA is that it's not possible to define columns using c:forEach, since the property names for sortBy and filterBy are incorrectly parsed/resolved. However, this seems not to be possible after all, as explained below.

        In the test case I developed, I define a set of columns in this way:

        <c:forEach var="col" items="#{tableBean.cols}">
        	<ace:column headerText="#{col.header}" sortBy="#{track[col.prop]}" filterBy="#{track[col.prop]}"/>
        </c:forEach>
        

        And in the backing bean I have this:

        	private List<Col> cols = null;
        	public List<Col> getCols() {
        		if (cols == null) {
        			cols = new ArrayList<Col>();
        			cols.add(new Col("Artist", "artist"));
        			cols.add(new Col("Album", "album"));
        			cols.add(new Col("Name", "name"));
        			cols.add(new Col("AMG", "AMGReview"));
        			cols.add(new Col("Pitchfork", "pitchforkReview"));
        			cols.add(new Col("iTunes", "iTunesSales"));
        			cols.add(new Col("Brick and Mortar", "bAndMSales"));
        		}
        		return cols;
        	}
        	public static class Col {
        		private String header;
        		private String prop;
        		public Col(String header, String prop) {
        			this.header = header;
        			this.prop = prop;
        		}
        		public String getHeader() {
        			return this.header;
        		}
        		public String getProp() {
        			return this.prop;
        		}
        	}
        

        In other words, there's a "column model" class (Col) that is used to dynamically define columns by specifying a header name and a property name, for sorting and filtering purposes, using c:forEach.

        The problem is that when using this approach, we cannot resolve the property names for sorting and filtering. With the current code, we simply pass a "prop]" string in all cases, because our parsing function doesn't account for dynamic EL expressions (or expressions containing the [] notation). Moreover, after trying to resolve these EL expressions properly, I found out that it's impossible; the returned value of evaluating such expression is always null. It seems to be due to the fact that the c:forEach variable ('col' in this case) is no longer in the ELContext object, since the c:forEach tag had already been processed to create those components. So, in this case, it's not possible to resolve the value of the 'col' variable. Besides that, there doesn't seem to be a way to partially evaluate a (dynamic) EL expression to obtain a new expression string that we could parse. Only the original expression string is available (in this case, '#{track[col.prop]}'). Also, using $ or # expressions makes no difference.

        I also tried pushing and popping the column components to/from EL when trying to evaluate the expressions, and I also tried creating new custom value expressions and try to resolve a simpler expression involving the c:forEach variable, but none of that worked.

        Show
        Arturo Zambrano added a comment - - edited After developing a test case and trying to add this improvement, I realized that it's not possible to accomplish this in the way we intended. For lazy loading cases, we simply pass the property name itself to the load method, so that the app developer can identify which column was filtered and/or sorted, since we can't do the filtering and sorting from our component code since the entire data set is not in memory. Thus, we allow developers to proceed in any way they want. For example, if a column is defined like this: <ace:column headerText= "Artist" sortBy= "#{track.artist}" filterBy= "#{track.artist}" /> We would pass the string 'artist' to the load() method of LazyDataModel as part of both the 'sortCriteria' and 'filters' objects. We do this by simply parsing the original expression string from the ValueExpression object and obtaining the property name. The problem described by this JIRA is that it's not possible to define columns using c:forEach, since the property names for sortBy and filterBy are incorrectly parsed/resolved. However, this seems not to be possible after all, as explained below. In the test case I developed, I define a set of columns in this way: <c:forEach var= "col" items= "#{tableBean.cols}" > <ace:column headerText= "#{col.header}" sortBy= "#{track[col.prop]}" filterBy= "#{track[col.prop]}" /> </c:forEach> And in the backing bean I have this: private List<Col> cols = null ; public List<Col> getCols() { if (cols == null ) { cols = new ArrayList<Col>(); cols.add( new Col( "Artist" , "artist" )); cols.add( new Col( "Album" , "album" )); cols.add( new Col( "Name" , "name" )); cols.add( new Col( "AMG" , "AMGReview" )); cols.add( new Col( "Pitchfork" , "pitchforkReview" )); cols.add( new Col( "iTunes" , "iTunesSales" )); cols.add( new Col( "Brick and Mortar" , "bAndMSales" )); } return cols; } public static class Col { private String header; private String prop; public Col( String header, String prop) { this .header = header; this .prop = prop; } public String getHeader() { return this .header; } public String getProp() { return this .prop; } } In other words, there's a "column model" class (Col) that is used to dynamically define columns by specifying a header name and a property name, for sorting and filtering purposes, using c:forEach. The problem is that when using this approach, we cannot resolve the property names for sorting and filtering. With the current code, we simply pass a "prop]" string in all cases, because our parsing function doesn't account for dynamic EL expressions (or expressions containing the [] notation). Moreover, after trying to resolve these EL expressions properly, I found out that it's impossible; the returned value of evaluating such expression is always null. It seems to be due to the fact that the c:forEach variable ('col' in this case) is no longer in the ELContext object, since the c:forEach tag had already been processed to create those components. So, in this case, it's not possible to resolve the value of the 'col' variable. Besides that, there doesn't seem to be a way to partially evaluate a (dynamic) EL expression to obtain a new expression string that we could parse. Only the original expression string is available (in this case, '#{track [col.prop] }'). Also, using $ or # expressions makes no difference. I also tried pushing and popping the column components to/from EL when trying to evaluate the expressions, and I also tried creating new custom value expressions and try to resolve a simpler expression involving the c:forEach variable, but none of that worked.
        Hide
        Arturo Zambrano added a comment - - edited

        A possible solution is to add two attributes to ace:column to cover this approach of defining columns. The attributes would be called something like 'filterByPropertyName' and 'sortByPropertyName'. They are intended to simply contain a string indicating the name of the property, which will be sent to the LazyDataModel.load() method, when the corresponding EL expression is not possible to parse/resolve. For example, in the test case described in the previous comment. These attributes would have the values "col.prop", which would be resolved to 'artist', 'album', 'name', etc. when c:forEach is processed, and would be passed to the load() method, when the original value string is complex (e.g. when using the [] notation).

        Markup example:

        <c:forEach var="col" items="#{tableBean.cols}">
        <ace:column headerText="#{col.header}" sortBy="#{track[col.prop]}" filterBy="#{track[col.prop]}"
        sortByPropertyName="#{col.prop}" filterByPropertyName="#{col.prop}"/>
        </c:forEach>
        
        Show
        Arturo Zambrano added a comment - - edited A possible solution is to add two attributes to ace:column to cover this approach of defining columns. The attributes would be called something like 'filterByPropertyName' and 'sortByPropertyName'. They are intended to simply contain a string indicating the name of the property, which will be sent to the LazyDataModel.load() method, when the corresponding EL expression is not possible to parse/resolve. For example, in the test case described in the previous comment. These attributes would have the values "col.prop", which would be resolved to 'artist', 'album', 'name', etc. when c:forEach is processed, and would be passed to the load() method, when the original value string is complex (e.g. when using the [] notation). Markup example: <c:forEach var= "col" items= "#{tableBean.cols}" > <ace:column headerText= "#{col.header}" sortBy= "#{track[col.prop]}" filterBy= "#{track[col.prop]}" sortByPropertyName= "#{col.prop}" filterByPropertyName= "#{col.prop}" /> </c:forEach>
        Hide
        Ted Goddard added a comment -
        Show
        Ted Goddard added a comment - See also: http://jira.icesoft.org/browse/ICE-8378
        Hide
        Arturo Zambrano added a comment - - edited

        Alternative, perhaps more intuitive, names for these attributes could be 'lazySortByKey' and 'lazyFilterByKey'. These resolved strings would simply be passed to the LazyDataModel.load() method:

        List<?> load(int first, int pageSize, SortCriteria[] sortCriteria, Map<String,String> filters)

        SortCriteria.getPropertyName() would return this string, and all the keys in the 'filters' map would be these strings as well. We normally extract these values (property names) from the EL expressions and set them to SortCriteria.propertyName and as the keys of the 'filters' map, but if these expressions are complex and these new attributes are defined, we would use these values instead. Basically, these attributes can be any string that helps the app developer identify which column's sorting and/or filtering functionality is in effect, in the load() method.

        Show
        Arturo Zambrano added a comment - - edited Alternative, perhaps more intuitive, names for these attributes could be 'lazySortByKey' and 'lazyFilterByKey'. These resolved strings would simply be passed to the LazyDataModel.load() method: List<?> load(int first, int pageSize, SortCriteria[] sortCriteria, Map<String,String> filters) SortCriteria.getPropertyName() would return this string, and all the keys in the 'filters' map would be these strings as well. We normally extract these values (property names) from the EL expressions and set them to SortCriteria.propertyName and as the keys of the 'filters' map, but if these expressions are complex and these new attributes are defined, we would use these values instead. Basically, these attributes can be any string that helps the app developer identify which column's sorting and/or filtering functionality is in effect, in the load() method.
        Hide
        Arturo Zambrano added a comment -

        Using ui:repeat instead of c:forEach just doesn't work. There are no errors in the server, but the columns don't get rendered.

        Show
        Arturo Zambrano added a comment - Using ui:repeat instead of c:forEach just doesn't work. There are no errors in the server, but the columns don't get rendered.
        Hide
        Arturo Zambrano added a comment -

        Committed alternative solution to 4.0 trunk at revision 40908 and to 3.3 EE maintenance branch at revision 40909. Added 'lazyColumnKey' attribute. This new attribute is used to specify a string that will be used to identify this column when working in lazy mode. This string will be passed as a key of the filters map and will be returned from SortCriteria.getPropertyName() in the LazyDataModel.load() method. If this attribute is defined, then this value will be used instead of the value obtained by parsing the EL expressions of the sortyBy and filterBy attributes. This attribute is useful when using complex and/or dynamic EL expressions that can't be parsed nor resolved at render time, such as when using c:forEach to define the columns.

        Show
        Arturo Zambrano added a comment - Committed alternative solution to 4.0 trunk at revision 40908 and to 3.3 EE maintenance branch at revision 40909. Added 'lazyColumnKey' attribute. This new attribute is used to specify a string that will be used to identify this column when working in lazy mode. This string will be passed as a key of the filters map and will be returned from SortCriteria.getPropertyName() in the LazyDataModel.load() method. If this attribute is defined, then this value will be used instead of the value obtained by parsing the EL expressions of the sortyBy and filterBy attributes. This attribute is useful when using complex and/or dynamic EL expressions that can't be parsed nor resolved at render time, such as when using c:forEach to define the columns.
        Hide
        Arturo Zambrano added a comment -

        Added known issue in the ace:dataTable page of the wiki and explained how to use this new attribute in cases where a table uses lazy loading and defines its columns using c:forEach.

        Show
        Arturo Zambrano added a comment - Added known issue in the ace:dataTable page of the wiki and explained how to use this new attribute in cases where a table uses lazy loading and defines its columns using c:forEach.

          People

          • Assignee:
            Arturo Zambrano
            Reporter:
            Judy Guglielmin
          • Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: