Select-all shortcut (Ctrl-A) in Vaadin Table? - vaadin

I have the following snippet in my UI Builder code:
table.addShortcutListener(new ShortcutListener("Select all", null, KeyCode.A, ModifierKey.CTRL) {
#Override
public void handleAction(Object sender, Object target) {
AbstractSelect t = (AbstractSelect) target;
if (t.isMultiSelect()) {
t.setValue(t.getItemIds());
}
}
});
return table;
This allows to press Ctrl+A to select all items in a table. This usually works the first time I load a view until I make one of the tables invisible (setVisible(false)). After making the tables visible again, it no longer works (not even on reloading the page) and I get the following console output whenever I press Ctrl+A:
WARNING: Ignoring action for disabled connector c.b.a.web.ui.builder.table.TranslatedHeaderTable
Nov 03, 2014 11:15:00 AM com.vaadin.event.ConnectorActionManager handleAction
What is wrong with my code? How would I achieve my goal?

I'd suggest this modification, works fine for me (i suppose comp is the Vaadin Table)
comp.addShortcutListener(new ShortcutListener("Select all", null, KeyCode.A, ModifierKey.CTRL) {
private static final long serialVersionUID = 1L;
#Override
public void handleAction(Object sender, Object target) {
if (comp.isMultiSelect()) {
comp.setValue(comp.getItemIds());
}
}
});
The problem might be, while testing locally, the failure of serialization of some component, so a static reference to comp (you'll need to make it final) and a defaut UID should do the trick.
tested multiple times and the error never occured.
Cheers.
EDIT
I understood that the problem occured when making invisible and then visible the table.
It came to my mind just now that you could have tried CTRL+A on an invisible Table: if this is the case so it's correct, when a Component is made invisible every listener is put in "Standby" until you make it visible again. So for me:
setVisible(false);
setVisible(true);
"CTRL+A"
works, while
setVisible(false);
"CTRL+A"
gives me
nov 03, 2014 2:19:34 PM com.vaadin.event.ConnectorActionManager handleAction
WARNING: Ignoring action for disabled connector com.vaadin.ui.Table
It's meant to be this way, nothing wrong in your code, you have to change your functionality and do not have a "CTRL+A" on an invisible Table (which seems a bad thing to do imho). On the other hand you could overwrite the setVisible method but I discourage it.
Cheers.

It seems there are some problems with the Table implementation of the Action.Notifier interface. In this Vaadin Forum Post, the Vaadin Devs suggest to add the ShortcutListener not to the Table itself but to a Panel that the Table is enclosed within.
My new implementation:
private void addCtrlAHandler(final AbstractSelect table) {
Panel panelEnclosingTable = table.findAncestor(Panel.class);
Preconditions.checkArgument(panelEnclosingTable != null, "Table is not enclosed in a panel; cannot add shortcut handlers");
panelEnclosingTable.addShortcutListener(new ShortcutListener("Select all", null, KeyCode.A, ModifierKey.CTRL) {
#Override
public void handleAction(Object sender, Object target) {
if (table.isMultiSelect()) {
table.setValue(table.getItemIds());
}
}
});
}
With this workaround, I get the expected behavior.

Related

Vaadin 23: BeforeLeaveEvent vs DialogCloseActionEvent

Context: In a Vaadin 23 application there is a form that is reachable directly via URL. It registers a BeforeLeaveListener with UI.getCurrent().addBeforeLeaveListener(bll);. The implementation of the BeforeLeaveListener is this:
#Override
public void beforeLeave(BeforeLeaveEvent event) {
ContinueNavigationAction action = event.postpone();
askAndProceedIfOk(() -> {action.proceed();});
}
It explicitly postpones the BeforeLeaveEvent and explicitly proceeds the event.
In the application there's a second form that is opened in a modal dialog with the option to close it.
Dialog modalDialog = new Dialog();
modalDialog.setCloseOnEsc(true);
modalDialog.setCloseOnOutsideClick(true);
modalDialog.setModal(true);
The BeforeLeaveListener doesn't work here, but a ComponentEventListener<Dialog.DialogCloseActionEvent> does:
modalDialog.addDialogCloseActionListener(new ComponentEventListener<Dialog.DialogCloseActionEvent>() {
#Override
public void onComponentEvent(Dialog.DialogCloseActionEvent event) {
myForm.askAndProceedIfOk(() -> {modalDialog.close();});
}
});
Surprise: I am surprised that BeforeLeaveEvent and DialogCloseActionEvent work different:
BeforeLeaveEvent offers postpone() and proceed()
DialogCloseActionEvent seems to be implicitly postponed just by showing another modal Dialog that asks the user whether to save unsaved changes. And I do have to close the modal dialog explicitly.
Question: Is it right that both events (with a similar task) work that different or do I miss some details here?

Detach event in Vaadin web app not getting called

My question stems from an issue almost identical to the one here (which did not end up getting a satisfactory answer):
https://vaadin.com/forum/thread/13932610
Like this person, I expected that upon closing the browser that my app was open in, a detach event would proc; however, this did not happen. I've tried adding a detach listener, overriding the detach method, and doing both at the same time, but none of them were successful. As for how I know the detach event was not called, my detach event is a simple print statement - that does not show up in the output.
Note that like in the aforementioned thread, I've already set the heartbeat interval (2 seconds in my case) and set closeIdelSessions to be true. So, I thought I would just have to wait six seconds, but that's certainly not been the case.
When I try this (find the essential parts of the code below), the detach() is eventually being called. I run this with Jetty, and I did not touch its default. It tooks some ~45 minutes after closing the Browser, when I saw "Detach called" logged on console. So yes, the time is lengthy. The reason is that the last UI is cleaned up only after HttpSession is expired (which depends on application container etc. settings). If you want to do forced clean up quicker, you need to use https://vaadin.com/directory/component/cleanupservlet-add-on
#Push
#SuppressWarnings("serial")
public class DemoUI extends UI {
#WebServlet(value = "/*", asyncSupported = true)
#VaadinServletConfiguration(productionMode = false, ui = DemoUI.class, heartbeatInterval=5, closeIdleSessions=true)
public static class Servlet extends VaadinServlet {
}
#Override
public void detach() {
System.out.println("Detach called");
}
#Override
protected void init(VaadinRequest vaadinRequest) {
...
}

How To Implement Scroll Listener In Vaadin 7 Tables

In Vaadin, when you scroll down or up in tables (com.vaadin.ui.Table) there is no event that will be fired to tell you that user is now scrolling.
Why would we need scroll event in table?
Let's first take a look at this example of Vaadin (Dashboard Demo) after you open the link just click sign in, and you'll be automatically redirected to the transactions page, this page shows a table of financial transactions, these transactions will show a batch after another while you are scrolling, what actually happened is that the data was loaded on UI initiation (all the transactions).
Now, let's assume that we have thousands or say millions of transactions. Is it logic to load them all together when the UI is initiated? isn't it of wisdom to load them bit by bit to prevent slowing down the UI while it waits for all transactions to load?.
The best solution to this problem is to get the first (say 100 transactions) then get more transactions while scrolling down, but this solution have only one problem, that is Vaadin does not support Scroll Event Handling in com.vaadin.ui.Table !! 
According to your question you are looking for lazy loading.
If you connect your table with a container which supports lazy loading your items are loaded from it's data source lazily. This means when you scroll and get out of the buffered items the table "asks" the container for more items. The container then loads more items from it's data source.
For example the JPAContainer from Vaadin supports that feature. This container connects to a data source using JPA. More information here.
Vaadin Table's Scrolling Event
The Dashboard Demo (GitHub repo) project actually depends on Vaadin tables as you see in the code in this file in line 53, what we are going to do is to extend com.vaadin.ui.Table and implement our own behavior that will support scrolling Vaadin tables from now on.
First of all, let's create new simple interface ScrollingTableScrollListener this interface will be responsible for implementing scroll events and its code will look like this:
package com.vaadin.demo.dashboard.scrolling;
public interface ScrollingTableScrollListener {
public void doTableScroll();
}
This interface should be implemented whenever you have a table in your view and you want to add a scroll event handler for it. But wait a minute this is not applicable to any kind of tables, this is only applicable to our own table.
Now, let's create our table, our table's name is  (ScrollingTable) and it extends (com.vaadin.ui.Table) this class code is:
package com.vaadin.demo.dashboard.scrolling;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.vaadin.ui.Table;
public class ScrollingTable extends Table {
private static final long serialVersionUID = 5007124121625961567L;
List listeners = new ArrayList();
private void fireSrollEvent() {
for (ScrollingTableScrollListener listener : listeners) {
listener.doTableScroll();
}
}
public void addScrollListener(ScrollingTableScrollListener listener) {
listeners.add(listener);
}
#Override
public void changeVariables(Object source, Map variables) {
super.changeVariables(source, variables);
fireSrollEvent();
}
}
Actually, this table is the same as Vaadin's one, but it has the following differences:
It overrides the method changeVariables() of the super class (Vaadin Table : com.vaadin.ui.Table), actually here lays our core business that will initiate the Scrolling Behavior. What we did here is that we invoked the changeVariables() of the super class and then we invoked the fireSrollEvent() method.
Another difference is that it has two more members:
public void addScrollListener(ScrollingTableScrollListener listener) this method will take care of adding new listeners to table's scrolling event.
private void fireSrollEvent() this method is the one that will be invoked by changeVariables() method and will invoke the method doTableScroll() on every registered listener that this table has added by invoking the method addScrollListener().
Now to make use of the new stuff that we have added, we will change the original code of the aforementioned Dashboard demo (specifically the file TransactionsView.java). In this file there are only few lines to add and modify.
First, we will modify the line 49 by adding new interface that this class will implement, which is our new interface (ScrollingTableScrollListener) and implement its single method by adding the following lines in the end of this class:
#Override
public void doTableScroll() {
// TODO Auto-generated method stub
Notification.show("You are scrolling!\nYou can add your own behavior here!");
}
Then, we will change both lines 53 and 66 to use the new inherited class (ScrollingTable) rather than the super class (Table):
//Line 53 in the original class
Table t;
//Line 55 in our class
ScrollingTable t;
....
....
....
//line 66 in the original class
t = new Table() {
//line 68 in our class
t = new ScrollingTable() {
Finally, we should add the listener to our ScrollingTable's scrolls :) , this is done by invoking the method addScrollListener on the table (t) after defining the table (line 89 of our new class):
t.addScrollListener(this);
this line means that this class (TransactionView) is listening on the ScrollingTable (t) scroll event, and it will invoke the method doTableScroll whenever the user scrolls down/up the ScrollingTable (t).
Here you go, you have now a table that will tell you whenever the user is scrolling, your task now is to do what you want when the table fires scrolling event and put your stuff between the curly brackets {} of the method that we defined in the first step:
#Override
public void doTableScroll() {
// Put your own code here ...
}
Here is the link of the new Dashboard on GitHub.
The answer on my blog
Hm, event fires on EVERY action with table (such as row select, etc.). More appropricate solution is additional checking variable changing in table state.
Here example:
public class ScrollingTable extends Table {
// ...
#Override
public void changeVariables(Object source, Map<String, Object> variables) {
super.changeVariables(source, variables);
handleScrollEvent(variables);
}
private void handleScrollEvent(Map<String, Object> variables) {
if (variables.containsKey("firstvisible")) {
for (TableScrollListener listener : listeners) {
listener.doTableScroll();
}
}
}

Role-based navigation display in MVC4 Bootstrap Sample

How are you supposed to conditionally display menu items based on roles in the Bootstrap Sample project? I was thinking of doing the following
Implement INavigatonRouteFilter - really just implementing the shouldRemove(Route navigationRoutes) method - by getting the default controller/action for the route and seeing if the user is authorized
Call NavigationRoutes.Filters.Add(myAuthorizationFilter) after configuring the NavigationRoutes in App_Start
There are two problems I see with this approach:
I don't actually know how to do the first step unless I add in a bunch of conditional statements to check for Controller's name explicitly
This seems like it could make NavigationRoutes.Filters very hard to deal with once there are a lot of filters or a desire for more modularity later on
I don't know that I've explained the problem clearly enough, but basically I want to use what is provided in the Bootstrap sample to implement authorization-based navigation menu display if at all possible. Using INavigationRouteFilter just seemed like the most natural way to do so.
For those looking for an answer or at least a quick fix.
Here's what I've come up with after 5 minutes and I most certainly haven't though about any side effects this may have.
routes.MapNavigationRoute<HomeController>("Index", c => c.Index())
.FilterRoute(() => !WebSecurity.IsAuthenticated);
You can either do all your filtering in your call to FilterRoute() or you can add more extension methods to save you some characters.
I'm thinking of .RequireRole("Adiministrators"); that calls WebSecurity.RequireRoles() in turn (or HttpContext.Current.User.IsInRole()) etc.
public static NavigationRouteBuilder FilterRoute(this NavigationRouteBuilder builder, Func<bool> func)
{
var currentRoute = builder._parent;
NavigationRoutes.Filters.Add(new BootstrapAuthorizationFilter(builder, x =>
{
if (x == currentRoute)
return func();
else
return false;
}));
return builder;
}
and BootstrapAuthorizationFilter is just a class implementing INavigationRouteFilter that calls func() in its ShouldRemove() method
public class BootstrapAuthorizationFilter : INavigationRouteFilter
{
private NavigationRouteBuilder builder;
private Func<NamedRoute, bool> func;
public BootstrapAuthorizationFilter(NavigationRouteBuilder builder, Func<NamedRoute, bool> func)
{
this.builder = builder;
this.func = func;
}
public bool ShouldRemove(Route navigationRoutes)
{
if (navigationRoutes is NamedRoute)
return func(navigationRoutes as NamedRoute);
return false;
}
}
Clearly nothing fancy and I'm not sure if I'd use it in production.
But I think is simple enough and works (for the cases I tested).
Having said that, I hope the new routing functionality is going to be released soon :)

Vaadin - Disable Column reordering for particular column

I'm doing a project in Vaadin 7.
In my project, I need to disable column reordering feature for particular columns in Treetable?
I'm really searching for function like this 'setColumnReorderIds()'.
Is it possible to do it in Vaadin 7.
Or else I need to write some code with 'ColumnReorderListener()'?
Update
This code is to set the first column fixed in a TreeTable. I want to disable reordering in Hierarchy column in the tree table.
public class CustomTreeTable extends TreeTable {
private static final long serialVersionUID = 1L;
private Object[] visibleColumns;
private KeyMapper<Object> columnIdMap = new KeyMapper<Object>();
#Override
public void paintContent(PaintTarget target) throws PaintException {
super.paintContent(target);
paintColumnOrder(target);
}
private void paintColumnOrder(PaintTarget target) throws PaintException {
visibleColumns = this.getVisibleColumns();
final String[] colorder = new String[visibleColumns.length];
int i = 0;
colorder[i++] = columnIdMap.key("Column 1"); // Logic to keep the first column fixed
for (Object colId : visibleColumns) {
if(!colId.equals("Column 1")) {
colorder[i++] = columnIdMap.key(colId);
}
}
target.addVariable(this, "columnorder", colorder);
}
}
Update 2
I tried what Oskar said..
In addition to
paintColumnOrder(target).
I'm calling
paintVisibleColumnOrder(target),
paintAvailableColumns(target),
paintVisibleColumns(target).
i'm able to stop reordering only for the table headers. But, the body is still reordering. Any guesses on this issue?
In the documentation there is only setColumnReorderingAllowed() which allows to control reordering of all columns. So if your case is to control particular ones it looks to me as a very custom behaviour and I would go with own implementation. Also ColumnReorderEvent is generated after processing the action itself therefore implementing own ColumnReorederListener won't help us here I think.
All actual magic which we want to change happens in private Table.paintColumnOrder() called from public Table.paintContent(), called from public TreeTable.paintContent() (see sources of Table and TreeTable). The solution would be:
extend TreeTable
override paintContent() with merged copies of Table.paintContent() and TreeTable.paintContent()
replace paintColumnOrder() call with your custom logic.
Update
Ok, now I see it's more tricky then I thought at the beginnig, since there is no easy way to access most of required fields and methods after subclassing TreeTable... Moreover, columns are reorered on the client side and only the change event status is sent to inform the server. I don't know how to handle custom reordering without creating custom gwt widget :(

Resources