Scrollbars

Hi all!

Is there a normal way to identify pressing the scroll bars in the table, the position of the thumb? Unfortunately, the official vaadin api, I did not find anything about it. I would like to have something like this:


table.addListener(new Table.ScrollClickListener() {
            private static final long serialVersionUID = -2644270772375060747L;
            @Override
            public void scrollClick(ScrollClickEvent event) {
                
            }
        })

Thanks in advance

No, there’s nothing really like that. You have to see if GWT supports something like that and extend the table if it does.

May I ask, why do you need to know when the user clicks on the scroller?

Sure! To load large data into table container exists 2 ways. Paged table and scrollable table (lazy looding). In my new project i need use second approach, then user scroll data in table and we detected then i need load additional data . But in the current implementation of the vaadin table when scrolling data with the scrollbar we do not receive any notification. Sorry for my bad english

You are trying to do lazy loading on a wrong layer. The container of your Table gets information about the rows the user is scrolled to. If you use JPA on your project, JPAContainer is a good choice for a lazy loading container. If you use pure SQL, check out SQLContainer. Also,
LazyQueryContainer
is worth of checking.

I do not mix UI with buisness layer. I use only @Remote interface to EJB.

I would then recommend writing a small back-end for LazyQueryContainer that uses your remote interfaces to load data when needed. While I haven’t done it myself, it should be simple enough.

It would be nice to see a small example. Thanks

I believe most examples of this would be in closed source applications, but take a look e.g. at the
mock implementation of queries
from LazyQueryContainer source repository. While it is meant for unit testing, it should demonstrate what needs to be implemented for a simple non-JPA back-end. Also read LQC documentation/javadoc.

You are right. So i did it. Here is the implementation:

@com.vaadin.ui.ClientWidget(VScrollableTable.class)
public class ScrollableTable extends Table {

	private static final long serialVersionUID = -8203570333788121152L;

	public ScrollableTable() {
		super();
	}

	@Override
	public void paintContent(PaintTarget target) throws PaintException {
		target.addAttribute("scrollPane", this);
		super.paintContent(target);
	}

	@Override
	public void changeVariables(Object source, Map<String, Object> variables) {

		super.changeVariables(source, variables);
		if (variables.containsKey("scrollPositions")) {

			Object[] scrollPositions = (Object[]
) variables.get("scrollPositions");
			
			int vPosition = (Integer) scrollPositions[0]
;
			int hPosition = (Integer) scrollPositions[1]
;
			if (hasListeners(TableScrollEvent.class)) {
				fireEvent(new TableScrollEvent(this, vPosition, hPosition));
			}

		}
	}

	/**
	 * Adds a table scrolling  listener. A table scroll listener is
	 * called when a user scroll rows.
	 * 
	 * @param listener
	 *            The listener to attach to the Table
	 */
	public void addListener(TableScrollListener listener) {
		addListener(VScrollableTable.TABLE_SCROLL_EVENT_ID,
				TableScrollEvent.class, listener, TableScrollEvent.METHOD);
	}

	/**
	 * Removes a table scroll listener from the Table.
	 * 
	 * @param listener
	 *            The listener to remove
	 */
	public void removeListener(TableScrollListener listener) {
		removeListener(VScrollableTable.TABLE_SCROLL_EVENT_ID,
				TableScrollEvent.class, listener);
	}

}
public class VScrollableTable extends VScrollTable{
	public static final String TABLE_SCROLL_EVENT_ID = "tableScroll";
    
    public VScrollableTable() {
        super();
    }

    @Override
    public void updateFromUIDL(final UIDL uidl, final ApplicationConnection client) {
        super.updateFromUIDL(uidl, client);
        String id = uidl.getStringAttribute("scrollPane");
        if (client != null && id != null) {
            
            final boolean immediate = uidl.getBooleanAttribute("immediate");
            
            VScrollTable scrollPane = (VScrollTable) client.getPaintable(id);
            final FocusableScrollPanel scrollBody = (FocusableScrollPanel) scrollPane.getWidget(1);
            
            scrollBody.addScrollHandler(new ScrollHandler() {
                @Override
                public void onScroll(ScrollEvent event) {
                    client.updateVariable(paintableId, "scrollPositions", new Object[] {scrollBody.getScrollPosition(), scrollBody.getHorizontalScrollPosition()}, immediate);
                }
            });
        }
    }
}
public class TableScrollEvent extends Component.Event {
    public static final Method METHOD;
    static {
        try {
            METHOD = TableScrollListener.class.getDeclaredMethod("scroll", new Class<?>[]{TableScrollEvent.class});
        } catch (final java.lang.NoSuchMethodException e) {
            // This should never happen
            throw new java.lang.RuntimeException(e);
        }
    }
    private static final long serialVersionUID = 1156474973981840889L;
    private int vPosition = 0;
    private int hPosition = 0;

    /**
     * Constructor
     *
     * @param source
     *            The source of the event
     */
    public TableScrollEvent(Component source, int vPosition, int hPosition) {
        super(source);
        this.vPosition = vPosition;
        this.hPosition = hPosition;
    }

	public int getVPosition() {
		return vPosition;
	}
	
	public int getHPosition() {
		return hPosition;
	}
    
}
public interface TableScrollListener extends Serializable {

    /**
     * Called when a user scroll table
     *
     * @param event
     *            The event which contains information about the scrolling
     *            action and thumb position
     */
    public void scroll(TableScrollEvent event);
    
}

Usage:


private ScrollableTable table = new ScrollableTable();
table.addListener(new TableScrollListener() {
			private static final long serialVersionUID = 8495405232090313654L;
			@Override
			public void scroll(TableScrollEvent event) {
				getMainWindow().showNotification("Scroll bar vposition: " + event.getVPosition() + " hposition: " + event.getHPosition());
			}
		});

It would be nice if you added something like this in the standard Table component

Upd:

But with increasing the number of records increasing number of calls to the server :frowning:

Maybe one option could be to group scroll events e.g. so that each scroll event (re)sets a timer, and the current value is sent on timeout. This might change the order of events the server sees (other events get delivered before scroll events), but there would only be at most one scroll event every 200 ms or whatever time you use.

Note that you might want to make scroll event sending always immediate by using “true” instead of the field “immediate” for this purpose.

Hi,

nice workaround. I have the same problem and i search for it some times.
Now i test your workaround but it don´t works.

I had to delete the @Override Statements by scroll and also the @com.vaadin.ui.ClientWidget(VScrollableTable.class) in ScrollableTable…

If i had it in i became a Error with the widget set displayed in Table.
If i had it without my table works, but the listener do nothing …

Can everybody help me ?

Edit:

It works now !

This is the problem: a new scroll handler is added on every update. It should only be done once.

Also, I think the “scrollPane” attribute should not be needed as you are in the correct client side instance already.

Hi All!
Excuse me for a long time did not participate in the discussion.
My research has shown that code fragment does not need:

@Override
    public void updateFromUIDL(final UIDL uidl, final ApplicationConnection client) {
        super.updateFromUIDL(uidl, client);
        String id = uidl.getStringAttribute("scrollPane");
        if (client != null && id != null) {
            
            final boolean immediate = uidl.getBooleanAttribute("immediate");
            
            VScrollTable scrollPane = (VScrollTable) client.getPaintable(id);
            final FocusableScrollPanel scrollBody = (FocusableScrollPanel) scrollPane.getWidget(1);
            
            scrollBody.addScrollHandler(new ScrollHandler() {
                @Override
                public void onScroll(ScrollEvent event) {
                    client.updateVariable(paintableId, "scrollPositions", new Object[] {scrollBody.getScrollPosition(), scrollBody.getHorizontalScrollPosition()}, immediate);
                }
            });
        }
    }

Why?

Because VScrollTable class already have a method onScroll.
We need only to overwrite it.


@Override
	public void onScroll(ScrollEvent event) {
		super.onScroll(event);
		Scheduler.get().scheduleFinally(new Command() {
				public void execute() {
					client.updateVariable(paintableId, "scrollPositions", new Object[] {event.getRelativeElement().getScrollTop(), event.getRelativeElement().getScrollLeft()}, true);
				}
			});
    	}

Thanks a lot!
With this update it works fine!