I'm writing a view which navigates to a table entry's page displayed on the left side when a table entry (on the right) is chosen. This is similar to the addressbook tutorial on Vaadin's site, only I make use of the Navigator and views.
While I got the navigation to work (clicking on entry with id #12 navigates to localhost:8080/test/12) and a test label in the view's enter() gets changed to match the id, testTable.getItem(event.getParameters()) returns null for some reason so I can't access the entry.
The ValueChangeListener and enter() for the view are shown below.
class ValueChangeListener implements Property.ValueChangeListener {
Object testId;
#Override
public void valueChange(ValueChangeEvent event) {
// Navigate to a chosen table entry
this.testId = event.getProperty().getValue();
navigator.navigateTo("test/" + testId);
}
}
...
public void enter(ViewChangeEvent event) {
Object tmp = event.getParameters();
testName.setValue((String) tmp); // is set to the id
System.out.println(testTable.getItem(tmp) == null); // DEBUG: always returns true
}
I think you should change this:
System.out.println(testTable.getItem(tmp) == null);
to this:
String str = (String) tmp;
if (str != null && !str.isEmpty()) {
System.out.println(testTable.getItem(Integer.parseInt(str)) == null);
}
I think there's something wrong in how you manage you Navigator.
Firstly when you change view with Navigator you who should add a proper "URL fragment" with "#".
For example the Vaadin sampler uses:
http://demo.vaadin.com/sampler/#foundation
If in your URL there's no "#" ViewChangeEvent.getParameters() gives you null or isEmpty().
Related
Let's say I load data into a Grid. That works perfectly, everything is displayed. I can see it just fine, even after I call editItem(objectId); to edit data for any given line in the Grid.
Then let's say I have a button that adds a new line with mostly empty elements. In other words, the corresponding bean is mostly empty except for some default values. For some reason this behaves weird when I call editItem(objectId);. Here are the situations and their results:
If column is editable (column.setEditable(true);), my default data displays just fine
If column is not editable (column.setEditable(false);), my default data does NOT display. It is definitely in the bean, just not displayed. I see it once I click "Save" or "Cancel" in the edit form.
If I just show the line, do NOT enter the edit form (don't call editItem(objectId);), it display the default data just fine. I mention this just to point out what happens in the "display only" situation.
I even tried making the editField read only, and even that hid the data. So what is happening?
Example of data being displayed (see circled red):
Figure 1: Non-Empty data, but editable column
Example of data NOT being displayed (see circled red):
Figure 2: Empty data inside edit form
Figure 3: Non-Empty data after clicking save.
Note that it does not matter if the column is a ComboBox or just a regular text element, if I make it non-editable, it will not show the value I put in that column on this new line until after I click Save or Cancel.
Here is how I load the list of beans initially, where gridContainer is defined as BeanItemContainer<T> gridContainer:
public void updateList( List<T> dataList, T defaultData ) {
updateList( dataList, defaultData, new GridLoader<T>() {
#Override
public void loadGrid(List<T> dataList) {
gridContainer.removeAllItems();
if( dataList instanceof List && !dataList.isEmpty() )
gridContainer.addAll(dataList);
}
});
}
This works fine for non-editable columns, all data being displayed as expected. My pictures sort of hide it, but it is displaying just fine, and works in edit mode just fine as well.
And here is how I add a new line:
private void addRouting() {
Routing emptyData = Routing.create();
emptyData.setKey(null);
if( facilityId instanceof String && !facilityId.trim().isEmpty() )
emptyData.setFacilityId(facilityId.trim());
if(wmsItem instanceof String && !wmsItem.trim().isEmpty())
{
emptyData.setWmsItem(wmsItem);
// gridComponent.hideColumn("wmsItem", true);
// gridComponent.hideColumn("wmsItemDisplay", false);
}
else
{
// gridComponent.hideColumn("wmsItem", false);
// gridComponent.hideColumn("wmsItemDisplay", true);
}
if(workCenter instanceof String && !workCenter.trim().isEmpty())
{
emptyData.setWorkCenter(workCenter);
// gridComponent.hideColumn("workCenter", true);
// gridComponent.hideColumn("workCenterDisplay", false);
}
else
{
// gridComponent.hideColumn("workCenter", false);
// gridComponent.hideColumn("workCenterDisplay", true);
}
gridComponent.addItemAt(0, emptyData);
gridComponent.select(emptyData);
if(gridComponent.isEditorEnabled())
gridComponent.editItem(emptyData);
}
And in GridComponent, we have addItemAt defined as follows (BTW, GridComponent just wraps a layout with a toolbar at top and a Grid for the data, and so exposes various methods I need from the toolbar and Grid):
public BeanItem<T> addItemAt(int index, T bean) throws IllegalStateException {
BeanItemContainer<T> gridContainer = getGridContainer();
if(gridContainer instanceof BeanItemContainer)
{
/* Clear filter first because adding an item will break this.
* TODO: Maybe restore prior filter with "saveLastFilter();" and then "reapplyLastFilter();" after "add"
*/
saveLastFilter();
clearFilter();
BeanItem<T> newItem = gridContainer.addItemAt(index, bean);
reapplyLastFilter();
return newItem;
}
else
throw new IllegalStateException("Missing bean grid container");
}
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;
}
}
I am facing problem in value change listener.
I have added value change listener in vaadin option group which has six checkboxes.
optionGroup.addValueChangeListener(this :: optionGroupValueChanged);
//..
private void optionGroupValueChanged(ValueChangeEvent valueChangeEvent) {
//...
}
Now I have one another checkbox which selects all the checkboxes of the option group (because it's multiselect). I want to avoid call of value change listener for individual checkboxes so that I first removed the value change listener and added after selecting all as stated below.
selectAllCheckBox.addValueChangeListener(this :: selectAllChecked);
//...
private void selectAllChecked(ValueChangeEvent valueChangeEvent) {
final boolean isChecked = (boolean) valueChangeEvent.getProperty().getValue();
//Following line does not remove the value change listener
optionGroup.removeValueChangeListener(this :: optionGroupValueChanged);
if(isChecked) {
//So here it will call value change of option group six time
optionGroup.getItemIds().stream().forEach( itemId -> optionGroup.select(itemId));
} else {
optionGroup.setValue(null);
}
optionGroup.addValueChangeListener(this :: optionGroupValueChanged);
}
I have checked code of vaadin removeValueChangeListener method it contains markAsDirty(); method. What is the reason of this behavior ? Is there any other alternative solution for my problem ?
Note : Version of vaadin is 7.5.0
That's because this :: optionGroupValueChanged creates each time new instance of ValueChangeListener. You don't want this, you want to remove very specific instance of ValueChangeListener. The solution is to remember (in private field in example) the reference to the listener and pass it in your add and remove ValueChangeListener calls.
optionGroupListener = this :: optionGroupValueChanged;
optionGroup.addValueChangeListener(optionGroupListener);
private void selectAllChecked(ValueChangeEvent valueChangeEvent) {
final boolean isChecked = (boolean) valueChangeEvent.getProperty().getValue();
//change here
optionGroup.removeValueChangeListener(optionGroupListener );
if(isChecked) {
optionGroup.getItemIds().stream().forEach( itemId -> optionGroup.select(itemId));
} else {
optionGroup.setValue(null);
}
//and here
optionGroup.addValueChangeListener(optionGroupListener );
}
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);
}
I noticed that vaadin 6.7.0 beta1 supports to add tooltip for row/cell of a table. However, I did not find any example how to add it.
Is there anybody who can provide some sample?
Use code as below:
table.setItemDescriptionGenerator(new ItemDescriptionGenerator() {
public String generateDescription(Component source, Object itemId, Object propertyId) {
if(propertyId == null){
return "Row description "+ itemId;
} else if(propertyId == COLUMN1_PROPERTY_ID) {
return "Cell description " + itemId +","+propertyId;
}
return null;
}}
You could accomplish this by setting a formfieldfactory. Here you could return a button that only loooks like text with styling CSS. This will let you set a caption on the button. This is obviously a ugly hack. More info about buttons and links in vaadin.
table.setTableFieldFactory(new TableFieldFactory() {
// container is the datasource
// item is the row
// property is the column
//
#Override
public Field createField(Container container, Object itemId, Object propertyId, Component uiContext) {
})
You can't add tooltpis (setDescription) to a row/cell nativly - not yet!
It is already in there issue tracker but don't know when they will implement this feature