[ROO-158] Allow sorting data in UI Created: 15/Jul/09  Updated: 04/May/12

Status: Open
Project: Spring Roo
Component/s: WEB MVC
Affects Version/s: None
Fix Version/s: None

Type: Improvement Priority: Major
Reporter: Raúl Arabaolaza Assignee: Rossen Stoyanchev
Resolution: Unresolved Votes: 53
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Attachments: Text File comparatorPatch.patch    
Reference URL: http://forum.springsource.org/showthread.php?t=74888

 Description   

Today´s in most production ready web applications users expect to be able to sort displayed data, currently roo´s generated listings don´t allow for sorting.

So entities should be made comparable with a estandard generated compareTo method like the actual toString method generated by roo, this way in the listings it would be easy to costomize general sorting of entities.

Also the UI should be able to allow sorting by field, like many Ui frameworks support out of the box.



 Comments   
Comment by Alexander Heusingfeld [ 07/Aug/09 ]

Hi

there is quite a quick fix for this issue. There is a very small javascript file which can be found here http://www.kryogenix.org/code/browser/sorttable/
All you have to do is
1. download the script and put it into /src/main/webapp/sorttable.js
2. add the javascript to the header.jsp like the following <script type="text/javascript" src="<c:url value="/static/sorttable.js"/>"> </script>
3. add the attribute class="sortable" to every table you like to have sortable

Works like a charm.

@Ben this is licensed unter X11, I'm too sure but I think this might even allow bundling with ROO? What do you think?

Comment by Raúl Arabaolaza [ 09/Aug/09 ]

While it would be very quick i see some problems with this approach
1. Java developers don´t have much control about sorting process, my opinion is that sorting entities should be done by using Comparable methods in server side
2. It creates a dependency to a concrete script instead of using Spring JS to decouple from concrete javascript libraries
3. Don´t male entities Comparable

Just an opinion.

What i would like to implement as long as the templating code in mvc addon and the addon apis become stable enough is something like:

1. Create a Comparable addon that will mark all my entities as Comparable and generate a field based comparator to allow sorting by field in server side by using a RooComparable annotation
2. Add a new method to the generated controller to sort the entities list by using the generated Comparator
3. Use Spring JS to add an AjaxEventDecorator to headers wich will triger the sort request

Regards, Raúl

Comment by Raúl Arabaolaza [ 31/Aug/09 ]

Hi,

I´ve develop the sort functionality at a basic level, it can be extended if you people think my aproach is worth the work.

First of all, i have created a new addon, called comparator, that will generate a comparator for every formBackingObject defined in the web scaffold annotation.

This generator will sort based on a field name, if the field is comparable it will use the compareTo method of the field, if not the field will be converted to string (using the toString method) and the resulting strings will be compared, here´s an example from a generated itd.

privileged aspect ChoiceComparator_Roo_Comparator {

declare parents: ChoiceComparator implements Comparator;

private java.lang.String ChoiceComparator.field = new java.lang.String();

public ChoiceComparator.new(String field)

{ super(); this.field = field; }

public int ChoiceComparator.compare(Object instance1, Object instance2) {
try {
Class comparableClass=Comparable.class;
Object value1=org.apache.commons.beanutils.PropertyUtils.getProperty(instance1, field);
Object value2=org.apache.commons.beanutils.PropertyUtils.getProperty(instance2, field);
if(comparableClass.isInstance(value1))

{ return ((Comparable)value1).compareTo(value2); }

else

{ return value1.toString().compareTo(value2.toString()); }


} catch (IllegalAccessException e)

{ throw new IllegalArgumentException("Unable to access property "+field+" accesor method",e); }

catch (java.lang.reflect.InvocationTargetException e)

{ throw new IllegalArgumentException("Unable to execute accesor method for property "+field,e); }

catch (NoSuchMethodException e)

{ throw new IllegalArgumentException("Unable to find accesor method for property "+field,e); }


}

}

This way i don´t need to generate a full fledged compareTo method wich can be dificult to implement as Ben said in the forum, and is compatible with all the JDK classes. if user wants to customize the sort for complex types (sorting by entities) he only needs to manually add the comparable interface to the entity, of course he also can modify the generated comparator or even the generated sort method.

Comparator creation will be trigerred by the webscaffold metadata when sort is not in the disallowedOperations (I´ve modify the annotations to add this value), also it will generate a controller method named sort.

Now the generated JSP will include as headers links to the sort method, again only if sort is not disallowed, and the propper springjavascript ajax decorators for that links.

Possible improvements include a cache mechanism for comparators (we don´t need to create more than one for field) and the ability to indicate a comparator to use insted of the generated one.

Feel free to contact me at [email protected] for futher details.

Path is attached, hope the patch is correctly generated, i´m not very used to create patches

Best Regards, Raúl

Comment by Shahzada Hatim [ 18/Oct/10 ]

I think there should be a common sort/filter/search plugin which can generate CRUD for all kinds of sorting. Such plugin could give a considerable boost to CRUD based apps.

Comment by Harald Walker [ 20/Oct/10 ]

Agree, that lists in default views should be sortable as option per column but sorting should happen in the database.

Comment by Domingo Gómez García [ 24/Apr/11 ]

I think the best approach is to refine a bit generated X_Roo_Entity.aj and X_Roo_Finder.aj with this change:

//Sample using my congress manager application

public static TypedQuery<Congress> Congress.findCongressesByOpeningBetween(Date minOpening, Date maxOpening)

{ if (minOpening == null) throw new IllegalArgumentException("The minOpening argument is required"); if (maxOpening == null) throw new IllegalArgumentException("The maxOpening argument is required"); EntityManager em = Congress.entityManager(); TypedQuery<Congress> q = em.createQuery("SELECT o FROM Congress AS o WHERE o.opening BETWEEN :minOpening AND :maxOpening", Congress.class); q.setParameter("minOpening", minOpening); q.setParameter("maxOpening", maxOpening); return q; }

To:
public static TypedQuery<Congress> Congress.findCongressesByOpeningBetween(Date minOpening, Date maxOpening, String sortField, boolean orderAsc)

{ if (sortField== null) throw new IllegalArgumentException("The sorting field argument is required"); if (minOpening == null) throw new IllegalArgumentException("The minOpening argument is required"); if (maxOpening == null) throw new IllegalArgumentException("The maxOpening argument is required"); EntityManager em = Congress.entityManager(); TypedQuery<Congress> q = em.createQuery("SELECT o FROM Congress AS o WHERE o.opening BETWEEN :minOpening AND :maxOpening ORDER BY o."+sortField+" "+(orderAsc?"ASC":"DESC"), Congress.class); q.setParameter("minOpening", minOpening); q.setParameter("maxOpening", maxOpening); return q; }

I don't know if's possible for a compiled query to use parameters for sorting instead of raw string concatenation like I did.
I'd like to see that changes included in roo by command option (maybe --sorting=true).

Regards.

Comment by Domingo Gómez García [ 24/Apr/11 ]

(Please delete previous comment without code format).
I think the best approach is to refine a bit generated X_Roo_Entity.aj and X_Roo_Finder.aj with this change:

Congress_Roo_Entity.aj
//Sample using my congress manager application

public static TypedQuery<Congress> Congress.findCongressesByOpeningBetween(Date minOpening, Date maxOpening) {
  if (minOpening == null) throw new IllegalArgumentException("The minOpening argument is required");
  if (maxOpening == null) throw new IllegalArgumentException("The maxOpening argument is required");
  EntityManager em = Congress.entityManager();
  TypedQuery<Congress> q = em.createQuery("SELECT o FROM Congress AS o WHERE o.opening BETWEEN :minOpening AND :maxOpening", Congress.class);
  q.setParameter("minOpening", minOpening); q.setParameter("maxOpening", maxOpening);
  return q;
 }

To:

Congress_Roo_Entity
public static TypedQuery<Congress> Congress.findCongressesByOpeningBetween(Date minOpening, Date maxOpening, String sortField, boolean orderAsc) { 
  if (sortField== null) throw new IllegalArgumentException("The sorting field argument is required");
  if (minOpening == null) throw new IllegalArgumentException("The minOpening argument is required");
  if (maxOpening == null) throw new IllegalArgumentException("The maxOpening argument is required");
  EntityManager em = Congress.entityManager();
  TypedQuery<Congress> q = em.createQuery("SELECT o FROM Congress AS o WHERE o.opening BETWEEN :minOpening AND :maxOpening ORDER BY o."+sortField+" "+(orderAsc?"ASC":"DESC"), Congress.class);
  q.setParameter("minOpening", minOpening);
  q.setParameter("maxOpening", maxOpening); 
  return q; 
}

I don't know if's possible for a compiled query to use parameters for sorting instead of raw string concatenation like I did.
I'd like to see that changes included in roo by command option (maybe --sorting=true).

Regards.

Comment by Basil Vandegriend [ 04/May/12 ]

This appears related to ROO-241 and ROO-3148.

Generated at Thu Sep 20 20:39:50 UTC 2018 using JIRA 7.9.2#79002-sha1:3bb15b68ecd99a30eb364c4c1a393359bcad6278.