Sort Grid Colunm with given Renderer does not work

If I have a Grid with to different Columns

  1. addColumn(b → b.getId()).setSortable(true)
  2. addColumn(new NumberRenderer<>(b → b.getId(), Locale.GERMANY)).setSortable(true)

Sorting by the 1. column works, but not for the 2nd column.
For the 2nd column only the sort indicator changes but the data will not be sorted.
I have tested this with Vaadin 14.1.25.
Is this a Vaadin bug or am I missing something?

When adding Grid columns via any other way than using property names (addColumn("id")), a working comparator should not be expected out-of-the-box (sometimes it still works, for example your first column). Setting the column sortable with setSortable(true) only allows the user to sort it (sorting indicator appears), but the column still doesn’t know how to be sorted in the first place.

This is where column.setComparator(...) comes into play. try this for your second column:

addColumn(new NumberRenderer<>(b -> b.getId(), Locale.GERMANY))
	.setComparator(Comparator.comparing(FooBar::getId)) // replace FooBar with the class used for grid items
	.setKey("id");

notice how you no longer need to call setSortable(true), since that is done within setComparator.

If you define a column with a Renderer, there’s not enough data for the DataProvider to know how it should sort itself when that particular column is clicked. You’ll need either a Comparator (which will only work with in-memory dataproviders) or explicitly define the sort property (which will also work with a lazy loading dataprovider) for manually implementing the sorting on dataprovider level. See https://vaadin.com/docs/v14/flow/components/tutorial-flow-grid.html for more information.

Thanks for the answers.
I have solved the problem with setComparator(), but I do not totally understand, why sorting with a ValueProvider works out of the box and sorting with a Renderer not.
A Renderer has a ValueProvider so the same logic could be used (Sorting by ValueProvider value).
In my opinion sorting a column is normally based on the raw value, not on the rendered value (e.g. Date).

A plain ValueProvider produces a single sortable value directly, but if you look at the root Renderer interface (which is what addColumn takes), you’ll find that it may contain multiple ValueProviders. You can see multiple ValueProviders used in practice e.g. in TemplateRenderers that combine multiple values to produce a HTML snippet. Thus it’s better that the developer explicitly chooses the way sorting should happen if there’s a Renderer in the column.

Alexander Ley:
sorting a column is normally based on the raw value, not on the rendered value (e.g. Date).

exactly. Since you are using a renderer, it does not know which raw value to compare. It cannot see that deep within your renderer you use item.getId() as raw value and therefore take the id as comparator. However if you added the column with addColumn("id"), it would know to use id property as value for comparison.

Yes, this can indeed lead to code that looks redundant. But because renderers can modify, format and wrap the raw values, vaadin cannot simply compare results of renderers. This is why you need to provide your own comparator when adding a rendered column.

Ok, I understand.
Perhaps the sorting magic behind addColumn (Comparator auto generated vs. not) should be documented somewhere. :wink:

Alexander Ley:
Ok, I understand.
Perhaps the sorting magic behind addColumn (Comparator auto generated vs. not) should be documented somewhere. :wink:

Yes, it is documented here: https://vaadin.com/docs/v14/flow/components/tutorial-flow-grid.html

This is a nice documentation, but as far as I have read it, it only describes what can be done but no word about the Renderer vs. ValueProvider difference. :wink:

The TemplateRenderer seems to support automatic sorting (with more than one ValueProvider):

For in-memory sorting to work correctly, the values returned by the ValueProviders in the TemplateRenderer (Person::getName and Person::getEmail in this example) should implement Comparable.

And about “Using a Comparator”:

If you need custom logic to compare items for sorting, or if your underlying data is not Comparable, you can set a Comparator for your column.

I think it should be documented in the API (e.g. Grid.Column.setSortable()) in which cases a user defined Comparator is needed. It seems not to be straight forward.

Alexander Ley:
This is a nice documentation, but as far as I have read it, it only describes what can be done but no word about the Renderer vs. ValueProvider difference. :wink:

The TemplateRenderer seems to support automatic sorting (with more than one ValueProvider):

For in-memory sorting to work correctly, the values returned by the ValueProviders in the TemplateRenderer (Person::getName and Person::getEmail in this example) should implement Comparable.

This part requires the context from above:

Example: Using the addColumn method with TemplateRenderer to set column sort properties.

grid.addColumn(TemplateRenderer.<Person> of(
        "<div>[[item.name]
]<br>" +
        "<small>[[item.email]
]</small></div>")
        .withProperty("name", Person::getName)
        .withProperty("email", Person::getEmail),
    "name", "email") // <---
    .setHeader("Person")
* For in-memory sorting to work correctly, the values returned by the ValueProviders in the TemplateRenderer (Person::getName and Person::getEmail in this example) should implement Comparable.

Here the sort properties are on the second to last row (the one with the two Strings, "name", "email") - the data is sorted first by name and then by email. Thus the Grid also needs to be created with the bean scanning new Grid(Person.class) constructor for the example to work.

You do kind of need to read between the lines to find out that Renderer columns are not automatically sortable; the main idea is stated here, though:

By default, all property-based columns are sortable, if the property type implements Comparable.

Many data types, such as String, Number, primitive types and Date/LocalDate/LocalDateTime are Comparable, and therefore also sortable, by default.

To make the column of a non-comparable property type sortable, you need to define a custom Comparator. See Column Sorting for more.

And about “Using a Comparator”:

If you need custom logic to compare items for sorting, or if your underlying data is not Comparable, you can set a Comparator for your column.

I think it should be documented in the API (e.g. Grid.Column.setSortable()) in which cases a user defined Comparator is needed. It seems not to be straight forward.

Not sure if setSortable’s JavaDoc is the right place, as columns are sortable by default with an in-memory dataprovider - there’s no need to call setSortable unless you’re calling it to disable sorting (which is the opposite of what the problem is) or maybe to re-enable sorting later after it has been disabled.

I would assume that a typical scenario for needing help is that you have tried a bunch of regular ValueProvider or property-based automatic columns first and you have seen that the sorting works there (without calling setSortable). Then when you’re adding something more complex with a Renderer, it suddenly creates a column that is no longer sortable, so maybe it would make most sense to have something mentioned in the JavaDoc of addColumn(Renderer). What do you think?

I think addColumn(Renderer) would be ok.
In the end I do not want to make such a big thing of this. :wink:

Here’s a ticket about improving the JavaDoc of addColumn(Renderer): https://github.com/vaadin/vaadin-grid-flow/issues/947