Vaadin Grid Row Index - vaadin

In a vaadin table if we do
table.setRowHeaderMode(RowHeaderMode.INDEX);
we get a column with the row index.
Is it possible to to the same with a vaadin grid?

So far I haven't seen such an option, but you should be able to fake it with a generated column. Please see below a naive implementation which should get you started (improvements and suggestions are more than welcome):
// our grid with a bean item container
Grid grid = new Grid();
BeanItemContainer<Person> container = new BeanItemContainer<>(Person.class);
// wrap the bean item container so we can generated a fake header column
GeneratedPropertyContainer wrappingContainer = new GeneratedPropertyContainer(container);
wrappingContainer.addGeneratedProperty("rowHeader", new PropertyValueGenerator<Long>() {
private long index = 0;
#Override
public Long getValue(Item item, Object itemId, Object propertyId) {
return index++;
}
#Override
public Class<Long> getType() {
return Long.class;
}
});
// assign the data source to the grid and set desired column order
grid.setContainerDataSource(wrappingContainer);
grid.setColumnOrder("rowHeader", "name", "surname");
// tweak it a bit - definitely needs more tweaking
grid.getColumn("rowHeader").setHeaderCaption("").setHidable(false).setEditable(false).setResizable(false).setWidth(30);
// freeze the fake header column to prevent it from scrolling horizontally
grid.setFrozenColumnCount(1);
// add dummy data
layout.addComponent(grid);
for (int i = 0; i < 20 ; i++) {
container.addBean(new Person("person " + i, "surname " + i));
}
This will generate something similar to the image below:

There is a Grid Renderer that can be used to do this now. It is in the grid renderers add-on https://vaadin.com/directory/component/grid-renderers-collection-for-vaadin7. It is compatible with Vaadin 8 as well.
Here is how it could be used (there are a few different options for how to render the index).
grid.addColumn(value -> "", new RowIndexRenderer()).setCaption("Row index");

Worth to mention that I use the following with Vaadin 18 flow and works perfectly.
grid.addColumn(TemplateRenderer.of("[[index]]")).setHeader("#");

Ok, it took me more than a while to figure this out. I don't know why you need this, but if your purpose is to find which grid row was clicked, then you can get the index from the datasource of your control via the itemClick event of your listener.
In my case, my datasource is an SQLContainer, and I already had it available (see ds var) so I did it this way:
grid.addListener(new ItemClickEvent.ItemClickListener() {
#Override
public void itemClick(ItemClickEvent event) {
Object itemId = event.getItemId();
int indexOfRow = ds.indexOfId(itemId);
}
});
You usually add a datasource to your control when you initialize it, via constructor or by setting the property. If you got you Grid from somewhere with an already-attached datasource, you can always get it with something like this:
SQLContainer ds = (SQLContainer)gred.getContainerDataSource();

I use this trick:
int i = 0;
grid.addComponentColumn(object -> {
i++;
return new Label("" + i);
}).setCaption("");

Related

Unkown Key in Vaadin 14 Grid during selection

I'm using a Grid in Vaadin 14. The grid is in multi-selection mode.
The selection handler takes a couple of seconds to complete and I'm calling setItems(...) at the end to update the items in the grid.
When the user selects another row while the previous selection handler is still running, I get an "Unknown key" error similar to the one described in https://github.com/vaadin/vaadin-grid-flow/issues/322, even though the new set of items still contains the selected item (another object instance but same according to equals()). This seems to be because the keys in the KeyMapper have already been changed due to setItems(), so the key coming from the client is not present anymore.
Is there a way to work around this, for example by disabling selection while the previous request is in progress?
UPDATE
To work around this Vaadin bug, I'm also calling setPageSize() with the exact number of items as argument. But it seems the same problem occurs even if I don't call setPageSize(), so it's probably due to setItems().
Do not change the grids items inside a SelectionListener.
You can still do all the things you wanted, but setting the items anew is not actually needed. In fact it will only create problems as you are experiencing now.
While working at this answer, I realized you will need to do your own Checkbox Column in order to be able to do actions for the one item that was just "selected", instead of removing all then add all selected ones (because much better performance). Here is how that could look.
// in my code samples, a `Foo` item can have many `Bar` items. The grid is of type Bar.
Grid.Column customSelectionColumn = grid.addComponentColumn(item -> {
Checkbox isSelected = new Checkbox();
isSelected.setValue(someParentFoo.getBars().contains(item));
isSelected.addValueChangeListener(event -> {
boolean newSelectedValue = event.getValue();
if(newSelectedValue){
someParentFoo.getBars().add(item)
} else {
someParentFoo.getBars().remove(item);
}
fooRepository.save(someParentFoo);
});
});
// make a Checkbox that selects all in the header
Checkbox toggleSelectAll = new Checkbox();
toggleSelectAll.addValueChangeListener(event -> {
if(event.getValue()){
someParentFoo.getBars().addAll(allGridItems);
} else {
someParentFoo.getBars().removeAll(allGridItems);
}
fooRepository.save(someParentFoo);
grid.getDataProvider().refreshAll(); // updates custom checkbox value of each item
});
gridHeaderRow.getCell(customSelectionColumn).setComponent(toggleSelectAll);
I solved this problem. Vaadin use data as key in HashMap. You need calc hashCode use immutable data fields. For example
public class TestData {
private int id;
private String name;
public TestData(int id) {
this.id = id;
}
#Override
public int hashCode() {
return Objects.hash(id);
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

Update autoComplete JavaFx?

I'm currently working on a JavaFX project.I'm using Autcomplete TextField of ControlFx .Each time i add new rows in database table, it should to update Autocomplete ,i did this but my problem is showing double Context-Menu ,we can say double autocompletes because i call method that create autocomplete each adding of new elements in table.
When i click a tab editBill i call this method :
public void showEditBill() {
if (!BillPane.getTabs().contains(EditBillTab)) {
BillPane.getTabs().add(EditBillTab);
}
SingleSelectionModel<Tab> selectionModel = BillPane.getSelectionModel();
selectionModel.select(EditBillTab);
/*it should remove the old autocomplete from textfield*/
pushBills(); //Call for cheking new items
}
pushBills method () :
public void pushBills() {
ArrayList list = new ArrayList<>();
bills = new BillHeaderDao().FindAll();
for (int i = 0; i < bills.size(); i++) {
list.add(bills.get(i).getIdClient());
}
//How can i remove the old bind before bind again
autoCompletionBinding = TextFields.bindAutoCompletion(SearchBill, SuggestionProvider.create(list));
}
How i can remove the old autocomplete and bind new automplete?
Just in any case if you need to keep instance of AutoCompletionTextFieldBinding object, thus avoiding use of:
autoCompleteBinding = TextFields.bindingAutoCompletion(TextField,List);
, which will change the instance, we could go a little bit deeper and use this:
// let's suppose initially we have this possible values:
Set<String> autoCompletions = new HashSet<>(Arrays.asList("A", "B", "C"));
SuggestionProvider<String> provider = SuggestionProvider.create(autoCompletions);
new AutoCompletionTextFieldBinding<>(textField, provider);
// and after some times, possible autoCompletions values has changed and now we have:
Set<String> filteredAutoCompletions = new HashSet<>(Arrays.asList("A", "B"));
provider.clearSuggestions();
provider.addPossibleSuggestions(filteredAutoCompletions);
So, through SuggestionProvider, we have "updated" auto completion values.
To avoid doubling of suggestions menu, don't use again (for the 2nd time):
TextFields.bindAutoCompletion(..)
In order to provide updates to the auto-complete suggestion list, retain a reference to the SuggestionProvider and update the suggestion provider instead:
TextField textField = new TextField();
SuggestionProvider suggestionProvider = SuggestionProvider.create(new ArrayList());
new AutoCompletionTextFieldBinding<>(textField, suggestionProvider);
When you want to update the suggestion list:
List<String> newSuggestions = new ArrayList();
//(add entries to list)
suggestionProvider.clearSuggestions();
suggestionProvider.addPossibleSuggestions(newSuggestions);
This will do the trick:
Instead of: TextFields.bindAutoCompletion(textField, list);
, try this:
List<String> strings = new ArrayList<>();
Then create binding between your textField with the list through:
new AutoCompletionTextFieldBinding<>(textField, SuggestionProvider.create(strings));
So any changes, including removing, from the list, will be reflected in the autoCompletion of the textField;
And you will have dynamic filtering of suggestions, showed in pop-up, when user enter some text in textField;
I had the same problem some time ago I try to do as #MaxKing mentions, but it didnt work. I managed to give it a soluciĆ³n even though I don't think it's the right way.
// Dispose the old binding and recreate a new binding
autoCompleteBinding.dispose();
autoCompleteBinding = TextFields.bindingAutoCompletion(TextField,List);
try this:
public void pushBills() {
ArrayList list = new ArrayList<>();
bills = new BillHeaderDao().FindAll();
for (int i = 0; i < bills.size(); i++) {
list.add(bills.get(i).getIdClient());
}
autoCompletionBinding.dispose();
autoCompletionBinding = TextFields.bindAutoCompletion(SearchBill, SuggestionProvider.create(list));
}

How to sort records (with code) in a grouped ListGrid?

This is the scenario: I'm working with a listgrid that needs to be grouped, and also needs to have its records ordered within each group. I've already used the ListGrid.sort() and the ListGrid.sort(String, SortDirection) methods but none of them works properly.
This problem doesn't show up when the grid isn't grouped (it makes the sort perfectly); and when the sort (with the listgrid is grouped) is made by clicking the column header, works fine but I need to sort it by code (without user interaction) because the header sort option needs to be disabled (and context menu too).
I'm using SmartGWT 4.0
Here is the class I'm using:
public class Access extends ListGrid {
public Access() {
super();
setWidth("30%");
setHeight100();
// setShowHeaderContextMenu(false);
setCanResizeFields(false);
// setCanSort(false);
setAutoFitWidthApproach(AutoFitWidthApproach.BOTH);
setWrapCells(true);
setFixedRecordHeights(false);
setShowRecordComponents(true);
setShowRecordComponentsByCell(true);
ListGridField id = new ListGridField("id", "ID");
ListGridField user = new ListGridField("user", "User");
ListGridField access = new ListGridField("access", "Access");
id.setHidden(true);
user.setWidth("60%");
access.setWidth("40%");
access.setType(ListGridFieldType.BOOLEAN);
access.setCanEdit(true);
setFields(id, user, access);
groupBy("access");
access.setGroupTitleRenderer(new GroupTitleRenderer() {
public String getGroupTitle(Object groupValue, GroupNode groupNode, ListGridField field, String fieldName,
ListGrid grid) {
return (String) groupValue + " - " + groupNode.getGroupMembers().length;
}
});
getField("access").setGroupValueFunction(new GroupValueFunction() {
public Object getGroupValue(Object value, ListGridRecord record, ListGridField field, String fieldName,
ListGrid grid) {
Boolean access = (Boolean) value;
if (access)
return "With access";
else
return "Without access";
}
});
ListGridRecord lgr1 = new ListGridRecord();
lgr1.setAttribute("id", 1);
lgr1.setAttribute("user", "ewgzx");
lgr1.setAttribute("access", true);
ListGridRecord lgr2 = new ListGridRecord();
lgr2.setAttribute("id", 2);
lgr2.setAttribute("user", "Bgfths");
lgr2.setAttribute("access", false);
ListGridRecord lgr3 = new ListGridRecord();
lgr3.setAttribute("id", 3);
lgr3.setAttribute("user", "utcvs");
lgr3.setAttribute("access", true);
ListGridRecord lgr4 = new ListGridRecord();
lgr4.setAttribute("id", 4);
lgr4.setAttribute("user", "gfdjxc");
lgr4.setAttribute("access", false);
ListGridRecord lgr5 = new ListGridRecord();
lgr5.setAttribute("id", 5);
lgr5.setAttribute("user", "763");
lgr5.setAttribute("access", true);
ListGridRecord lgr6 = new ListGridRecord();
lgr6.setAttribute("id", 6);
lgr6.setAttribute("user", "2");
lgr6.setAttribute("access", false);
ListGridRecord lgr7 = new ListGridRecord();
lgr7.setAttribute("id", 7);
lgr7.setAttribute("user", "35");
lgr7.setAttribute("access", false);
ListGridRecord lgr8 = new ListGridRecord();
lgr8.setAttribute("id", 8);
lgr8.setAttribute("user", "123");
lgr8.setAttribute("access", true);
ListGridRecord lgr9 = new ListGridRecord();
lgr9.setAttribute("id", 9);
lgr9.setAttribute("user", "2342");
lgr9.setAttribute("access", true);
ListGridRecord lgr10 = new ListGridRecord();
lgr10.setAttribute("id", 10);
lgr10.setAttribute("user", "aqwc");
lgr10.setAttribute("access", false);
setRecords(new ListGridRecord[] { lgr1, lgr2, lgr3, lgr4, lgr5, lgr6, lgr7, lgr8, lgr9, lgr10 });
sort("user", SortDirection.ASCENDING);
}
}
I have been having a similar issue. Disclaimer: if the "grouping data" message is not appearing when you group then the following solution may not help.
In my case the sorting of a grouped column was screwed because of the "grouping data" pop up.
Let me clarify.
The "grouping data" pop up appears when trying to group a ListGrid that is displaying more than 50 records.
It appears because the ListGrid, internally, is doing the grouping operation asynchronously to avoid the "script running slowly" message from the browser.
What I did was to set the grouping async threshold to a higher value. The risk of doing this is getting the "script running slowly" browser message, even though this is likely to happen only with IE8/9.
In the end , in the grid constructor, just add (I used 500 as a threshold):
setInitialSort(new SortSpecifier[] {new SortSpecifier("user", SortDirection.ASCENDING)}));
setGroupByField("access");
setGroupByAsyncThreshold(500);
Also set the initial sort and the grouped column as shown above.
PROGRAMMATICALLY, FIRST SORT, THEN GROUP.
Hope this helps.
This is due to sort() being called before rendering the grid, and setRecords() complicates things further.
Initial rendering of the grid happens along with its parents when rootCanvas.draw() is called (in onModuleLoad or similar).
As setRecords() can be used to change data set in the grid anytime, it tries to redraw the grid regardless of whether its initial stage or not.
If in the real scenario, sort is triggered after UI initialization, it should work as given in following code sample.
Remove the sort() call at the end of the constructor.
final Access access = new Access();
Button button = new Button("Sort");
button.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
// toggle sort direction, using two different ways to do it
SortSpecifier sortSpecifier = access.getSortSpecifier("user");
if (sortSpecifier == null || SortDirection.DESCENDING.equals(sortSpecifier.getSortDirection())) {
access.sort("user", SortDirection.ASCENDING);
} else {
access.setSort(new SortSpecifier[]{
new SortSpecifier("user", SortDirection.DESCENDING)
});
}
}
});
Check http://www.smartclient.com/smartgwt/showcase/#grid_multilevel_sort to see how to use listGrid.setInitialSort().
Having setRecords() in the constructor could lead to other initialization issues as well.
Update
To have the grid grouped by and sorted on load, set an initial sort and a group by field as indicated below.
// along with other configuration methods, can not use after grid is drawn
SortSpecifier sortSpecifier = new SortSpecifier("user", SortDirection.ASCENDING);
setInitialSort(new SortSpecifier[]{sortSpecifier});
// use following instead of groupBy(), which is used to group the grid programmatically
// groupBy() causes a redraw
setGroupByField("access");
An overloaded ListGrid.setGroupByField(String... field) method is available to group by multiple fields.

java.lang.IllegalStateException: trying to requery an already closed cursor android.database.sqlite.SQLiteCursor#

I've read several related posts and even posted and answer here but it seems like I was not able to solve the problem.
I have 3 Activities:
Act1 (main)
Act2
Act3
When going back and forth Act1->Act2 and Act2->Act1 I get no issues
When going Act2->Act3 I get no issues
When going Act3->Act2 I get occasional crashes with the following error: java.lang.IllegalStateException: trying to requery an already closed cursor android.database.sqlite.SQLiteCursor#.... This is a ListView cursor.
What I tried:
1. Adding stopManagingCursor(currentCursor);to the onPause() of Act2 so I stop managing the cursor when leaving Act2 to Act3
protected void onPause()
{
Log.i(getClass().getName() + ".onPause", "Hi!");
super.onPause();
saveState();
//Make sure you get rid of the cursor when leaving to another Activity
//Prevents: ...Unable to resume activity... trying to requery an already closed cursor
Cursor currentCursor = ((SimpleCursorAdapter)getListAdapter()).getCursor();
stopManagingCursor(currentCursor);
}
When returning back from Act3 to Act2 I do the following:
private void populateCompetitorsListView()
{
ListAdapter currentListAdapter = getListAdapter();
Cursor currentCursor = null;
Cursor tournamentStocksCursor = null;
if(currentListAdapter != null)
{
currentCursor = ((SimpleCursorAdapter)currentListAdapter).getCursor();
if(currentCursor != null)
{
//might be redundant, not sure
stopManagingCursor(currentCursor);
// Get all of the stocks from the database and create the item list
tournamentStocksCursor = mDbHelper.retrieveTrounamentStocks(mTournamentRowId);
((SimpleCursorAdapter)currentListAdapter).changeCursor(tournamentStocksCursor);
}
else
{
tournamentStocksCursor = mDbHelper.retrieveTrounamentStocks(mTournamentRowId);
}
}
else
{
tournamentStocksCursor = mDbHelper.retrieveTrounamentStocks(mTournamentRowId);
}
startManagingCursor(tournamentStocksCursor);
//Create an array to specify the fields we want to display in the list (only name)
String[] from = new String[] {StournamentConstants.TblStocks.COLUMN_NAME, StournamentConstants.TblTournamentsStocks.COLUMN_SCORE};
// and an array of the fields we want to bind those fields to (in this case just name)
int[] to = new int[]{R.id.competitor_name, R.id.competitor_score};
// Now create an array adapter and set it to display using our row
SimpleCursorAdapter tournamentStocks = new SimpleCursorAdapter(this, R.layout.competitor_row, tournamentStocksCursor, from, to);
//tournamentStocks.convertToString(tournamentStocksCursor);
setListAdapter(tournamentStocks);
}
So I make sure I invalidate the cursor and use a different one. I found out that when I go Act3->Act2 the system will sometimes use the same cursor for the List View and sometimes it will have a different one.
This is hard to debug and I was never able to catch a crashing system while debugging. I suspect this has to do with the time it takes to debug (long) and the time it takes to run the app (much shorter, no pause due to breakpoints).
In Act2 I use the following Intent and expect no result:
protected void onListItemClick(ListView l, View v, int position, long id)
{
super.onListItemClick(l, v, position, id);
Intent intent = new Intent(this, ActivityCompetitorDetails.class);
intent.putExtra(StournamentConstants.App.competitorId, id);
intent.putExtra(StournamentConstants.App.tournamentId, mTournamentRowId);
startActivity(intent);
}
Moving Act1->Act2 Act2->Act1 never gives me trouble. There I use startActivityForResult(intent, ACTIVITY_EDIT); and I am not sure - could this be the source of my trouble?
I would be grateful if anyone could shed some light on this subject. I am interested in learning some more about this subject.
Thanks,D.
I call this a 2 dimensional problem: two things were responsible for this crash:
1. I used startManagingCursor(mItemCursor); where I shouldn't have.
2. I forgot to initCursorAdapter() (for autocomplete) on onResume()
//#SuppressWarnings("deprecation")
private void initCursorAdapter()
{
mItemCursor = mDbHelper.getCompetitorsCursor("");
startManagingCursor(mItemCursor); //<= this is bad!
mCursorAdapter = new CompetitorAdapter(getApplicationContext(), mItemCursor);
initItemFilter();
}
Now it seems to work fine. I hope so...
Put this it may work for you:
#Override
protected void onRestart() {
// TODO Auto-generated method stub
super.onRestart();
orderCursor.requery();
}
This also works
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
startManagingCursor(Cursor);
}

how to use binding from a grid to an HTML template?

is it possible to use GXT's binding mechanism to bind a Grid to an Html (or other Widget implementation for that matter)?
it seems from the examples that it's only applicable for forms (FormBinding), while my purpose is to have a Template updating a widgets inner element, driven by the grid's SelectionChangedEvents sending the models for that template.
Binding grids and generic components directly is apparently not possible at the current version of GXT, anyhow this is an alternative solution offered as a workaround:
I just made use of the examples Grid Binding and Templates to support your requirement. Here is the modified code
#Override
protected void onRender(Element parent, int index) {
super.onRender(parent, index);
setStyleAttribute("margin", "10px");
ContentPanel cp = new ContentPanel();
cp.setHeading("Form Bindings");
cp.setFrame(true);
cp.setSize(800, 400);
cp.setLayout(new RowLayout(Orientation.HORIZONTAL));
final LayoutContainer panel = new LayoutContainer(new FlowLayout());
Grid<Stock> grid = createGrid();
grid.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
final Template template = new Template(getTemplate());
grid.getSelectionModel().addListener(Events.SelectionChange, new Listener<SelectionChangedEvent<Stock>>() {
public void handleEvent(SelectionChangedEvent<Stock> be) {
if (be.getSelection().size() > 0) {
template.overwrite(panel.getElement(), Util.getJsObject(be.getSelectedItem()));
} else {
panel.removeAll();
}
}
});
cp.add(grid, new RowData(.6, 1));
cp.add(panel, new RowData(.4, 1));
add(cp);
}
private native String getTemplate() /*-{
var html = [ '<p>Name: {name}</p>', '<p>Symbol: {symbol}</p>',
'<p>Last: {last}</p>', '<p>Last Updated: {date}</p>', ];
return html.join("");
}-*/;
Add these modifications to the Grid Binding example. Hope this helps.
Thanks,
Ganesh

Resources