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 :(
Related
How to check in Vaadin 7 if scrollbar is visible or not for a certain component, for example for Panel
Any implementation of AbstractClientConnector can be extended with AbstractExtension: https://vaadin.com/api/com/vaadin/server/AbstractExtension.html
An extension is a possible way to extend the functionality of your component: https://vaadin.com/docs/-/part/framework/gwt/gwt-extension.html
Adding features to existing components by extending them by inheritance creates a problem when you want to combine such features. For example, one add-on could add spell-check to a TextField, while another could add client-side validation. Combining such add-on features would be difficult if not impossible. You might also want to add a feature to several or even to all components, but extending all of them by inheritance is not really an option. Vaadin includes a component plug-in mechanism for these purposes. Such plug-ins are simply called extensions.
In the client-side extension implementation you can write your custom GWT code like following (pseudo code):
#Override
protected void extend(ServerConnector target) {
// Get the extended widget
final Widget widget = ((ComponentConnector) target).getWidget();
// register RPCs
YourServerRpcImplementation serverRpc = getRpcProxy(YourServerRpcImplementation.class); // client to server
registerRpc(YourClientRpcImplementation.class, this); // server to client, unused in this example
// add listener and update server state
Window.addResizeHandler(new ResizeHandler() {
#Override
public void onResize(ResizeEvent event) {
boolean scrollbarVisible = widget.getElement().getScrollHeight() > widget.getElement().getClientHeight();
serverRpc.yourEventMethod(scrollbarVisible);
}
});
}
Passing events between server and client: https://vaadin.com/docs/-/part/framework/gwt/gwt-rpc.html
I have a Grid component with Indexed container where data is sorted initially into two columns (the data comes from Elasticsearch). How can I tell Grid to know about this sorting and then show proper sorting icons in column headings when Grid is loaded?
EDIT #1
I tried to redefine Grids public List<SortOrder> getSortOrder() method to return proper sorting data, but it doesnt work ...
You haven't shown much code so I'll have to assume you're using the default Indexed container and then add columns like grid.addColumn("c1", String.class);. If that's true, then it looks like you can't supply your own container implementation because vaadin will throw an exception when providing a different container and adding columns (haven't had the time to figure out why):
protected void addColumnProperty(Object propertyId, Class<?> type, Object defaultValue) throws IllegalStateException {
if(!this.defaultContainer) {
throw new IllegalStateException("Container for this Grid is not a default container from Grid() constructor");
In this case, you can probably create your own Grid implementation which bypasses the issue, or you could use a bean item container like in the example below, where you overwrite the doSort method to do nothing:
BeanItemContainer<MyBean> dataSource = new BeanItemContainer<MyBean>(MyBean.class) {
#Override
protected void doSort() {
// nop, data is already sorted
}
};
Grid grid = new Grid(dataSource);
Then simply call the setSortOrder method on the grid.
import com.vaadin.data.sort.Sort;
import com.vaadin.shared.data.sort.SortDirection;
...
grid.setSortOrder(Sort.by("c3", SortDirection.ASCENDING).then("c2", SortDirection.DESCENDING).build());
Which results in column c3 being sorted ascending first, and then c2 descending (see the numbers near the sorting icon):
Please note that the column headers will still be clickable so the sorting icons will change and events will be triggered, but nothing will happen because the doSort was overwritten. You will probably need to react to this so you can add a sortListener and request a new batch of data, sorted according to the user selection:
grid.addSortListener(new SortEvent.SortListener() {
#Override
public void sort(SortEvent sortEvent) {
// request data sorted according to the user selection
}
});
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.
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();
}
}
}
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 :)