Remove value change listener - vaadin

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 );
}

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;
}
}

how to retain expand/collapse state in jqgrid grouping?

I have implemented a jqgrid in grouping method. By default I have kept the groups collapsed using groupCollapse:true parameter of jqgrid. My grid works well but When I expand the group and sort a column, the whole grid is reloaded and the expanded state of the column is not retained. How can I retain the expanded state while sorting?
Please write always which version of jqGrid, which you use (can use), and from which fork (free jqGrid, commercial Guriddo jqGrid JS or an old jqGrid in version <=4.7).
Your requirements could be easy realized in "free jqGrid", which I develop. It allows to use groupCollapse as callback function, which returns Boolean (see the issue). In combination with onClickGroup callback or jqGridGroupingClickGroup event one can easy persist the grouping state.
UPDATED: I created the demo https://jsfiddle.net/92da8xhq/, which demonstrates how one can persist the collapsing state in the grouping grid. Below I describe shortly the code. The demo uses one level of grouping to make the code more simple for understanding.
I added custom collapsedGroups: {} parameter to jqGrid. We will use the parameter to hold the list of collapsed groups. I used collapsedGroups: { "test2": true } in the demo to demonstrated that we can create the grid with some collapsed groups at the beginning. We don't use the value of the property of collapsedGroups object. Just the existence of the property test2 for example means that the group with the value test2 has collapsed state.
The demo uses groupCollapse property of groupingView defined as the callback function. The function tests whether the group is in the list of collapsed groups (has collapsedGroups property with some value)
groupingView: {
groupField: ["name"],
groupCollapse: function (options) {
var collapsedGroups = $(this).jqGrid("getGridParam", "collapsedGroups") || {};
// options looks like { group: number, rowid: string }
if (collapsedGroups.hasOwnProperty(options.group.value)) {
return true;
}
return false;
}
}
We adjust additionally the properties of the custom collapsedGroups parameter after expanding/collapsing of the group. We use the following onClickGroup callback:
onClickGroup: function (hid, isCollapsed) {
var p = $(this).jqGrid("getGridParam"),
iGroup = $(this).jqGrid("getGroupHeaderIndex", hid),
group = p.groupingView.groups[iGroup];
if (p.collapsedGroups == null) {
// be sure that the custom parameter is initialized as an empty object
p.collapsedGroups = {};
}
if (isCollapsed) {
// we place group.value in the p.collapsedGroups object as a property
if (!p.collapsedGroups.hasOwnProperty(group.value)) {
// create the property group.value in with some value
p.collapsedGroups[group.value] = true;
}
} else if (p.collapsedGroups.hasOwnProperty(group.value)) {
// remove group.value property from the p.collapsedGroups object
delete p.collapsedGroups[group.value];
}
}
groupingView: {
groupCollapse: true,
groupField: ["name"],
plusicon: 'ace-icon fa fa-plus-square purple',
minusicon: 'ace-icon fa fa-edit red'
}

How does the Vaadin table work with Navigator?

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().

how to remove a node from linked list using recursion?

I need to remove a node from a linked list using recursion. this is the code that I have so far...
public class SortedSetNode implements Set {
protected String value;
protected SortedSetNode next;
public boolean remove(String element) {
if (value.equals(element))
{
next = next.getNext();
return true;
}
else
{
return next.remove(element);
}
}
Well, without knowing what the problem is that you are facing, you would need a clause in there to check whether the item you are removing is actually in the linked list, i.e.
if(next == null){
return false;
}
Other than that your code looks fine. What is the issue you are encountering?
If the value attribute is the value of the current node, then you'll need to delete itself when value equals element, rather than delete the next. Unless it's the value of next node.
You might need a start point, so when you compare value, you compare the next node's value with the string, and if found, do your next = next.getNext();. Of course a check of null is needed.

How to make my ListBox not to call SelectionChanged event, when I assign ItemSource of my list

I have a combobox, that I populate from a web service:
public Configure()
{
InitializeComponent();
WebServiceSoapClient ws = new WebServiceSoapClient();
ws.GetTypesCompleted += new EventHandler<GetTypesCompletedEventArgs>(OnGetTypeCompleted);
ws.GetTypesAsync();
}
void OnGetTypeCompleted(object sender, GetTypesCompletedEventArgs e)
{
if (e.Result != null)
{
List<CodeTableItem> source = e.Result.ToList<CodeTableItem>();
lstType.ItemsSource = source;
lstType.SelectedIndex = -1;
}
}
So when I set the ItemSource property, SelectionChanged event gets fired with SelectedIndex = 0, but user hasn't made this selection yet and I need this list to have no selected value, so I'm setting SelectedIndex to -1, as you can see. As a result, SelectionChanged is called twice.
Can I make it be called only when user selects the item?
Thanks!
I'm using Silverlight 3 and VS 2008
Instead, modify your code so that the SelectionChange event handler isn't defined until after the itemssource and selected index are set.
void OnGetTypeCompleted(object sender, GetTypesCompletedEventArgs e)
{
if (e.Result != null)
{
List<CodeTableItem> source = e.Result.ToList<CodeTableItem>();
lstType.ItemsSource = source;
lstType.SelectedIndex = -1;
lstType.SelectionChanged += new SelectionChangedEventHandler(lstType_SelectionChanged);
}
}
In our application we implemented some code that would set a boolean flag based on the Control.LeftMouseButtonUp() event. When this has been set, it would mean that the user has interacted with the field, and so we can handle the SelectionChanged with different behaviour.
Over the development lifetime of our application this approach was essential so that default bindings would trigger our SelectionChanged logic when we didn't want it to.
If you are an MVVM purist, you'll need to expose the VM as a member variable and then set the bool flag in the VM.
HTH,
Mark

Resources