Grid: First impressions

In a word: Excellent!

Once again, many thanks to the Vaadin team who have worked hard to bring us some more magic. I spent the weekend converting the first of my 25 Tables to Grid. The flexibility and functionality were everyting Vaadin promised. For example, the ability to design both the look and the function of filters is great. A full conversion to Grid will be possible only after the user gets the column resize feature back.

A few comments:

  1. If I were a Vaadin designer I might have put a bit more time into setting friendly defaults for header and footer parameters. The button and textfield components would be placed too close to the top and bottom edges such that their selected “glow” gets cut off. In order to place them as shown in the FirstGrid attachment, the header and footer heights had to be increased to 30px and the components put on an AbsoluteLayout/CustomComponent so the positions could be tweaked. I might have thought that standard Valo components would fit nicely into Grid header and footer rows out of the box.

  2. There is an issue with column widths. If a filter is made such that there are no rows selected, the column widths collapse (preumably to some default) and don’t re-expand when the filters are reset and data again populates the rows. (GridCutoff.png below). I’ve used explicit .setWidth(xx) so don’t expect any width changes for any reason. I think others have reported similar findings.

  3. Initial rendering of the grid is incomplete. When the tab containing the grid is selected, about 3/4 of the rows are initially rendered, then nothing until the next interaction with the server. It’s as if there is a missing setImmediate(true) somewhere, but I can’t find any way to make it render all at once.
    18448.png
    18449.png

I have a similar problem as reported in the point 2 of Steve Demy
Attached 2 files showing the layouting in debug mode. It shows that the grid is effectively using all the available space, the inner contents somehow gets collapsed.

Somehow (I stil didn’t took the time to find out), it expands again taking the full red box showing in the layout after some contents are showed. Will update asap
18450.png
18451.png

Regarding item #3, the rendering problem was caused by the choice of height settings:

        setHeightMode(HeightMode.ROW);
        setHeightByRows(20d);

That causes the initial render to be incomplete, completed only on the next server call, which in my case is a regular push from another UI.

Replacing the ROW height mode with a pixel-based height setting fixed it.

        setHeight(550, Unit.PIXELS);

Is this a bug in the way setHeightMode works?

Just to note, the point 2 is not fixed in latest Vaadin 7.4.1 release.
Grid Component still have headers colapsed/srinked to some default size, even when there is data, the whole content gets shrinked as the attachments of previous posts shows.

I am using Mac/Crome, but it also fails on mac safari and Firefox

Vaadin provides a work-around in 7.4.1 that addresses point #2.

When updating filters as in the book example, we

  1. Remove the old filters
  2. Add new filters.

Now we can

  1. Remove the old filters

grid.recalculateColumnWidths(); 2) Add new filters

recalculateColumnWidths() is new to 7.4.1 and has solved point #2 for me.

I am using this grid.recalculateColumnWidths() method, but it’s not working for me.

We have to call this method every time we think we have no data in the table?
When excatly does grid srinks the columns ?
I wouuld like to create a subclass from Grid and catch the parts where I have to call this method instead of going throguh all the places where it needs to be added.

Thanks for the answer btw!

Besides, if you are using jpacontainer, normally you do applyFilters() which does the step 1 and 2 you are describing…

I agree, the grid should not shrink up by itself. My use case is not much affected because the one line of recalculation code is in a GridHelper class that is involved in the generation of each filter. So the one line of code solves the problem system-wide.

I pasted an excerpt from my helper class below. It might not make much sense out of context. This is an inner class that is instantiated once for each filter and takes a few configuration arguments. You can see I’ve implemented the recalculate in the helper and a little absolute layout to solve problem #1 above.

The helper is called like this: The “this” is a class that extends Grid. descriptionInput is the Grid column getting the filter.

        HeaderCell descriptionInput = filterRow.getCell(descriptionColumn.getPropertyId());
        GridHelper<DefectGrid>.TextFieldFilter     descriptionFilter     = gridHelper.new TextFieldFilter();
        descriptionInput.setComponent(descriptionFilter.textfieldFilter(this, descriptionColumn.getPropertyId(), 100));        

The inner class within my helper looks like this. I’m not a programmer by trade so try no to laugh too hard. :slight_smile: It won’t run as-is due to references to external stuff, but hopefully you get the idea: I send a pointer to Grid in to the filter builder where the recalculate line works.

    // inner class creates a text field filter
    public class TextFieldFilter {
    
        private TextField filterField = new TextField();
        private List<SimpleStringFilter>     textFilterList            = Collections.synchronizedList(new ArrayList<SimpleStringFilter>());
        // null constructor
        public TextFieldFilter () { }
    
        // returns a dynamic textfield filter
        public CustomComponent textfieldFilter (Grid grid, Object propertyID, int width) {

            int height = 25;

            // create the textfield used for filter entries            
            filterField.setWidth(width, Unit.PIXELS);
            filterField.setHeight(height, Unit.PIXELS);
            filterField.setMaxLength(30);   // to limit the length of the filter to a reasonable number of characters
            filterField.setStyleName("filterorange");

            // update the filter when the field is changed programmatically
            filterField.addValueChangeListener(change -> {
                textChange(grid, filterField.getValue(), propertyID);
            });

            // update the filter when the field is changed manually
            filterField.addTextChangeListener(change -> {                
                textChange(grid, change.getText(), propertyID);
            });

            // arrange the components in a layout
            AbsoluteLayout layout = new AbsoluteLayout();
            layout.setHeight(height + 4, Unit.PIXELS);
            layout.setWidth(width + 4, Unit.PIXELS);
            layout.addComponent(filterField, "left: 2px; top: 2px;");

            return (new CustomComponent(layout));    
        }

        private void textChange (Grid grid, String newValue, Object propertyID) {
            // a temporary list used to store the filters marked for removal
            List<SimpleStringFilter> toBeRemoved = new ArrayList<SimpleStringFilter>();
            
            // start by removing any existing filter on this column
            for (SimpleStringFilter filter : textFilterList ) {
                if ( filter.getPropertyId().equals(propertyID) ) {
                    if (sqlcontainer != null) { sqlcontainer.removeContainerFilter(filter); }
                    toBeRemoved.add(filter);
                }
            }            
            textFilterList.removeAll(toBeRemoved);
            toBeRemoved.clear();

            // due to this problem, which may be soon solved: https://vaadin.com/forum#!/thread/9391521  (#2)
            // if the problem does get resolved, the reference to grid in this class can be removed.
            grid.recalculateColumnWidths();           

            // if the filter field isn't empty, make a new filter representing the currently entered text
            if (newValue != null && !("").equals(newValue) ) {
                SimpleStringFilter updatedFilter = new SimpleStringFilter(propertyID, newValue, true, false);
                if (sqlcontainer != null) { sqlcontainer.addContainerFilter( updatedFilter ); }
                textFilterList.add(updatedFilter);
            }

            evaluateReportButtonVisibility();

            // generate a summary for the table footer
            sendFooterMessage();
        }

        public synchronized void setTextFilter(String filterSetting) {
            filterField.setValue(filterSetting);        
        }
     
        public synchronized List<SimpleStringFilter> getTextFilterList() {
            return textFilterList;
        }
    }

I understand. Thanks for taking the time to explain.

My problem is a little more complicated because I use JpaContainer, and either and there are at least 2 places where I should look into to use the recalculateColumnWidths() which is after the container.refresh() and container.applyFilters() in the places where I use applyFilters(), but jpacontainer also auto filters if you change filters on the fly and I don’t have an abstraction around the places where I do use filtering api of the container.

However, I will try to see if I can come up with a workaround, anyway there is this other issue regarding compact styling of the table that still needs to be fixed until it looks nice to me :slight_smile:
http://dev.vaadin.com/ticket/16937

At least as the version 7.4.1 makes it inconvenient for me to use Grid with JPAContainer.

7.4.2 has cracked the column width problem. Thanks Leif. (
#16734
)

I was also struggling with a series of exceptions caused when container refresh cycles involved removing all items and re-populating them. Those were solved with 7.4.2. There is obviously a lot going on at Vaadin right now.

Today’s list of things that could use improvement:

  • There is no good way to preserve a grid’s state when a refresh is being pushed to it. Most of my grids are based on SQL containers which work fine, but some grids use data assembled from multiple places into IndexedContainers used as the source. The only sensible way to refresh those is to remove all container items and re-build them. That works and performs fine but affords no good way to preserve the state of the Grid selection, the filters and the sorting during the refresh. Many lines of code has it mostly working. I haven’t been able to find a way to read the state of sorting.
  • It would be nice to easily distinguish between a manual, a programmatic and a system-generated row selection change. The selection listener catches them all, but requires some work around code to try to figure out which are which, and mask the listener as required. getSource() notifies us that the selection changes are all from Grid, but no further details.

Overall, I remain very happy with the functionality and flexibility of Grid. Vaadin’s hard work is making it better every day.

Version 7.4.2 (FF 36.0.1)
I just create small test to play a little with grid.
In multiselect mode if I modify column set the top selector checkbox disappears.
If I hit F5 it is painted again.

Test code: VerticalLayout l = new VerticalLayout(); IndexedContainer container = new IndexedContainer(); final String first = "First"; final String second = "Second"; container.addContainerProperty(first, Integer.class, null); container.addContainerProperty(second, Integer.class, null); for (int i = 0; i <= 10; i++) { Item item = container.addItem(i); item.getItemProperty(first).setValue(i); item.getItemProperty(second).setValue(10 - i); } Button b = new Button("Change cols in the grid"); l.addComponent(b); b.setData(first); final Grid grid = new Grid("Grid"); grid.setContainerDataSource(container); grid.setSelectionMode(SelectionMode.MULTI); b.addClickListener(new Button.ClickListener() { @Override public void buttonClick(ClickEvent event) { Button b = event.getButton(); grid.removeAllColumns(); grid.addColumn(first).setHeaderCaption(first); if (b.getData() == first) { b.setData(second); grid.addColumn(second).setHeaderCaption(second); } else { b.setData(first); } } }); b.click(); l.addComponent(grid)
18710.png

It would also be nice if you change StaticCell.getCellState() to be public because now only way to detect is there component in header cell or not is try to get it and catch IllegalStateException.
Rather than catching an excption I like to do.

HeaderCell cell = this.filteringRow.getCell(propertyId);
if(cell.getCellState().type == GridStaticCellType.WIDGET){
 //I know that component is available
}

Not really,

I am using latest 7.4.2 and Grid still has some width issues… (see attachment)

18713.png

You are right, it hasn’t solved everything, but it is good progress and solved my use case. Leif has done further work for 7.4.3 in the form of
#17091
. Maybe that will solve your issues when it’s released?

Will surely check :slight_smile:

Grid is comming really amazing!

To work around #3 in my original post above, I set Grid height manually, using code like this:

        int showRows = Math.min(defectContainer.size(), 23);  // <-- change this to adjust the max rows shown
        int gridHeight = 26 + 30 + 30 + (showRows * 26 );    // <-- headers, footer + ( n * one row height )    
        setHeight(gridHeight, Unit.PIXELS);

It’s called after a container refresh to update the Grid height and thus position the footer close to the data. But a problem has arisen with Windows machines. A horizontal scroll bar is placed at the bottom of the Grid - contributing to the total Grid height which is then subtracted from the height available for rows. This is shown in the attachments using a one row Grid. The Grid is shown rendered on OSX (correctly) and on Windows (shrunken rows). Are there any suggestions for how to elegantly handle this?
18751.png
18752.png

grid.recalculateColumnWidths();

works for me… Thank you.

Hi,

I am using grid table in my project as well as filter table .I have  some problem here that --> in filter table when we clicking on the table header ,first row will be automatically highlighting but coming to grid table ,it is not happening as filter table  and also when we click on grid cell it is selecting that cell only but in filter table when we click on any cell it will select entire row ,I want the same action in grid table 
 I want same behaviour as filter table in grid table how can I achieve it,is there is any possibility to do like that ,any code change or any method i can use .

     please give me any solution or any code change to fix  this bug . 
     here is the url for grid table demo::[b]

https://vaadin.com/grid
[/b]
And iam using the below code in my grid table::

    [b]

setWidth(“100%”);
setSelectionMode(SelectionMode.SINGLE);
setImmediate(true);
setSizeFull();
setContainerDataSource(dataSource);
setFooterVisible(true);
for (Map.Entry<String, Object> entry : defHdrRow.entrySet()) {
getDefaultHeaderRow().join(entry.getValue()).setText(entry.getKey());
}
for (Map.Entry<String, Object> entry : prependHdrRow.entrySet()) {
prependHeaderRow().join(entry.getValue()).setText(entry.getKey());
}
}
[/b]