Vaadin - Table column order - vaadin

Anybody know how to/or it is possible - create a Table with column specific order; configuration order which was before save - example in DB,
and uploaded at specific view on? also I wonder how to take generate this columns headers and content from POJOS class - beans.
Any good ideas?

setVisibleColumns
The Table::setVisibleColumns does double-duty:
Controls which columns are visible, and
Sets the order in which the columns appear.
Call Table::getVisibleColumns to see current ordering.
Doc
This is well described in:
Book of Vaadin > Table
Sampler > User Interface > Data Presentation > Table
Table API JavaDoc
Example Code
Basically, you need the code like this to control columns order and also set list of bean instances as datasource.
Code is not tested, just a demonstration. Valid for Vaadin 6, but I guess no significant changes comparing to Vaadin 7.
table = new Table();
// Wrap your beans collection into vaadin data container. There are many
// types of them , check Book of Vaadin.
BeanItemContainer<Bean> container = new BeanItemContainer<Bean>(Bean.class)
container.addBean(new Bean());
// Set collection of your beans as data source.
// Columns will be created for each property, vaadin uses reflection.
table.setContainerDataSource( container );
// You can select to display only properties you want, not all.
// Order matters. You can get columns list from any source - for example
// store in your DB.
table.setVisibleColumns( new Object[] {"prop1", "prop2"} );
// You can set column headers (by default vaadin will set them same as bean
// properties names). Order matters, should match above order.
table.setColumnHeaders( new String[] {"Property 1", "Property2"} );

The answer by Sergey Makarov is correct. This answer provides further information.
User’s Reordering
You may allow the user to drag-and-drop columns in a table to re-order them at runtime. To enable this feature, call isColumnReorderingAllowed.
You can use a listener to be informed when such a reorder event occurs.
The user’s re-ordering lasts only for this work session. If you want to maintain the user’s order in future work sessions, you must somehow persist their desired order and apply the order when instantiating the table again.
Losing The Order
If you replace the data source of the table, your column order will be reset. You can get the current order before the change, then restore. Example code follows.
// Visible status & ordering.
java.lang.Object[] visibleColumns = this.exampleTable.getVisibleColumns();
// ------| Fresh Data |--------------
// Put fresh data into the Table.
this.exampleTable.setContainerDataSource( freshBeanItemContainer );
// ------| Restore Config |--------------
// Visible status & ordering.
this.exampleTable.setVisibleColumns( visibleColumns ); // Assumes the table has the same properties.
By the way, as with visibility and order, being collapsed will also be reset with fresh data. You may want to track and restore column collapsing as well as ordering.

Related

Vaadin Grid (Multiselect): restore selection after refresh

A Vaadin grid shows data which is constantly updated by a background process. A user might select one or more rows to carry out various functions. The user might refresh the data from the backend (which updates rows shown in the grid).
The application needs to restore the selected items after a grid refresh.
grid.getSelectedItems() has to return the current instance of the selected items.
Refresh is implemented as follows:
void refresh() {
final var beanSet = grid.getSelectedItems();
dataProvider.refreshAll(); // refresh from backend
grid.asMultiSelect().select(beanSet); // restore previously selected items
}
Updating the grid works fine, but the selection is only partly restored: the "selected" checkbox is checked for the items in beanSet but querying grid.getSelectedItems() still returns the old instances.
Reproducer: https://github.com/skiedrowski/vaadin-grid-restore-selection, package com.example.application.views.idstyle -> check the notification after clicking "Update selected".
What is the correct way to update the selected items?
Context:
Vaadin Flow 23, Grid Pro in multiselect mode
grid items implement equals and hashCode based on an immutable id
grid data provider is a ConfigurableFilterDataProvider fetching paged data from backend
I believe the problem in your sample project is that you're always recycling a set of selected objects which contain the "old" data. You read out a reference to the old items in var beanSet = grid.getSelectedItems(); and store them back into the selection with grid.asMultiSelect().select(beanSet);
A lazy-loading Grid can't know if the programmatically set selection is a collection of objects that are available in the backend - it would need to fetch the entire backing dataset to do that. So when the selection is updated from the server, it could be any objects of the correct type, whether they actually exist in the data set or not.
What could do yourself is pass the selection set to the backend, update the items and then pass them back to the Grid's selection.
An open question that remains is whether Grid should update the selection when a fetch returns items that are equal to items in the selection. I can't immediately tell if that is
a) possible, or
b) a sensible thing to do
This is how we solve this in our application:
A]
fresh items are retrieved lazily
the only time when refreshed selection is needed is when we want to operate with the selected items (such as edit or another action, otherwise obsolete selected items don't matter)
the backend is able to return fresh item by ID
there is no need to update the selection on fetch() (which could introduce inconsistencies if a part of the selection is already updated but the rest haven't been fetched yet)
B]
we have some data providers which just hold wrappers for actual items
so any interaction with the items fetches fresh data under the hood
for the record, this was not done to solve this problem but mitigates it as a sideeffect

Vaadin Grid - Attempted to fetch more items from server than allowed in one go

~1500 data need to be fetched from DB. The code is pretty simple
List<Item> itemList = getItemsFromDB();
Grid<Item> grid = new Grid<>();
grid.addColumn(Item::getID).setHeader("Id").setAutoWidth(true);
grid.addColumn(Item::getName).setHeader("Name").setAutoWidth(true);
grid.setAllRowsVisible(true);
grid.setItems(itemList);
And I got this warning, only the first 1000 data are shown in the grid, the rest are just empty rows.
2022-04-15 15:46:52.475 WARN 19642 --- [nio-8080-exec-6] c.v.flow.data.provider.DataCommunicator : Attempted to fetch more items from server than allowed in one go: number of items requested '1583', maximum items allowed '1000'.
I know I can use lazy loading, but can I do it without it? The size of data will always be around 1500, and I don't actually care that much about how slow it is.
I am using vaadin 14.8.8
The problem comes from setAllRowsVisible and the DoS protection added inside the data provider. It's currently not easily possible to overwrite this without creating your own data provider.
I'm not really sure why you need all the rows visible; this increases the load time for your end user. If your only reason is to have the grid full height, you can just call setHeight on the grid and the let Vaadin handle the callbacks to the server to only fetch the amount of data needed to show on the client.

How do you get the column order for the Grid in Vaadin 14?

In Vaadin 8 you could just do the following for example to get a list of the columns in the order displayed on the screen.
String columnOrderPreference = ((List<Grid.Column>)grid.getColumns()).stream()
.map(Grid.Column::getId)
.collect(Collectors.joining(VALUE_SEPARATOR));
This was especially handy because you could then save that string and then call:
grid.setColumnOrder(columnOrderPreference.split(VALUE_SEPARATOR));
In Vaadin 14 (ignoring that getId() should now use getKey()) this is no longer possible because the getColumns() list is now always returned in the order they were added and not the order in which they are ordered. You can still call setColumnOrder(...) (with different parameters - list of grid.getColumnByKey(columnKey)) but I cannot figure out how to get the list of columns in the order they are displayed.
This is especially useful for trying to save the column order the user has set/changed when they come back to the same page (with the Grid).
You can listen for
ColumnReorderEvent
on the grid.
Registration addColumnReorderListener(ComponentEventListener<ColumnReorderEvent<T>> listener)
The event holds:
/* Gets the new order of the columns. */
List<Grid.Column<T>> getColumns()
Unfortunately in Vaadin 14 getColumns does not return the columns in the right order. You can get the order when with the event GridReorderEvent as said before and you need to store it. There is a feature request here ( that will give you some technical reasons if you're interested): https://github.com/vaadin/flow-components/issues/1315
You can add a comment or vote for it, because it makes the migration from Vaadin 8 harder.

OData Data read- latest entry on top

I am using OData API to read data in my Fiori Application. The issue is, in Odata API, the latest data entry is at the end rather it should be at the top. How do i do that ie put my latest data on top.
You can use the $orderby to decide what order the data is returned in. See the docs for more info. This URL is an example of ordering (using the OData TripPin example service) that sorts by the LastName property:
http://services.odata.org/V4/TripPinServiceRW/People?$orderby=LastName
We can use this same process to order by a DateTime value or an ID value to get your latest entries at the top. For example, here we order by the DateTimeOffset field StartsAt putting the latest entries first:
http://services.odata.org/V4/TripPinServiceRW/People('russellwhyte')/Trips?$orderby=StartsAt desc
1)
As mentioned before, you might have a look at server side sorting using “$orderby” as seen here.
2)
You might also want to check out the following tutorial on Sorting:
“
items="{
path : 'invoice>/Invoices',
sorter : {
path : 'ProductName'
}
}"
We add a declarative sorter to our binding syntax.
As usual, we transform the simple binding syntax to the object notation, specify the path to the data,
and now add an additional sorter property.
We specify the data path by which the invoice items should be sorted, the rest is done automatically.
By default, the sorting is ascending, but you could also add a property descending with the value true inside the sorter property to change the sorting order.”
Please see here and here
3)
This here might also be helpful:
“In this step, we will create a button at the top of the table which will change the sorting of the table.
When the current sorting state of the table is changed, the sorting state will be reflected in the URL.
This illustrates how to make the table sorting bookmarkable.”
Step 13: Make Table Sorting Bookmarkable
Sample: Navigation - Step 13 - Make Table Sorting Bookmarkable
4)
These links here also look interesting:
Sorting, Grouping and Filtering for Aggregation Binding
Sample: Sorting
Sample: With Sorting and Filtering Feature

Dynamic Tag Management - Storing

We're in the process of moving to DTM implementation. We have several variables that are being defined on page. I understand I can make these variables available in DTM through data elements. Can I simply set up a data elem
So set data elements
%prop1% = s.prop1
%prop2% = s.prop2
etc
And then under global rules set
s.prop1 = %s.prop1%
s.prop2 = %s.prop2%
etc
for every single evar, sprop, event, product so they populate whenever they are set on a particular page. Good idea or terrible idea? It seems like a pretty bulky approach which raises some alarm bells. Another option would be to write something that pushes everything to the datalayer, but that seems like essentially the same approach with a redundant step when they can be grabbed directly.
Basically I want DTM to access any and all variables that are currently being set with on-page code, and my understanding is that in order to do that they must be stored in a data element first. Does anyone have any insight into this?
I use this spec for setting up data layers: Data Layer Standard
We create data elements for each key that we use from the standard data layer. For example, page name is stored here
digitalData.page.pageInfo.pageName
We create a data element and standardize the names to this format "page.pageInfo.pageName"
Within each variable field, you access it with the %page.pageInfo.pageName% notation. Also, within javascript of rule tags, you can use this:
_satellite.getVar('page.pageInfo.pageName')
It's a bit unwieldy at times but it allows you to separate the development of the data layer and tag manager tags completely.
One thing to note, make sure your data layer is complete and loaded before you call the satellite library.
If you are moving from a legacy s_code implementation to DTM, it is a good best practice to remove all existing "on page" code (including the reference to the s_code file) and create a "data layer" that contains the data from the eVars and props on the page. Then DTM can reference the object on the page and you can create data elements that map to variables.
Here's an example of a data layer:
<script type="text/javascript">
DDO = {} // Data Layer Object Created
DDO.specVersion = "1.0";
DDO.pageData = {
"pageName":"My Page Name",
"pageSiteSection":"Home",
"pageType":"Section Front",
"pageHier":"DTM Test|Home|Section Front"
},
DDO.siteData = {
"siteCountry":"us",
"siteRegion":"unknown",
"siteLanguage":"en",
"siteFormat":"Desktop"
}
</script>
The next step would be to create data elements that directly reference the values in the object. For example, if I wanted to create a data element that mapped to the page name element in my data layer I would do the following in DTM:
Create a new data element called "pageName"
Select the type as "JS Object"
In the path field I will reference the path to the page name in my data layer example above - DDO.pageData.pageName
Save the data element
Now this data element can be referenced in any variable field within any rule by simply typing a '%'. DTM will find any existing data elements and you can select them.
I also wrote about a simple script you can add to your implementation to help with your data layer validation.Validate your DTM Data Layer with this simple script
Hope this helps.

Resources