MaterialFX
MaterialFX copied to clipboard
Table listener for row selection
In a non MFX table in order to observe for row selection you would use something like this:
table.getSelectionModel().selectedItemProperty().addListener((observableValue, oldValue, newValue) -> {
// logic here
});
with MFXTable I tried the following:
table.getSelectionModel().getSelection()
.addListener((MapChangeListener<Integer, Person>) change -> {
// logic here
}));
But I'm not getting any response on row selection/change
My goals are:
- By default some buttons are disabled and I want them to be enabled only when a row from the table is selected.
- A label is updated on row selection/change.
So far I have tried this:
table.setOnMouseClicked(e -> {
// logic here
});
But the event is only triggered when you click on the TableView object, therefore, when you click on TableRow it is not invoked.
Thank you for your good work!
Could you provide a minimal reproducible example?
@Tech-Expert-Wizard The fxml I have is the following:
<AnchorPane xmlns="http://javafx.com/javafx/18" xmlns:fx="http://javafx.com/fxml/1"
fx:id="childRoot" prefHeight="800" prefWidth="800"
fx:controller="com.cn5004ap.payroll.controller.employees.ListController">
<stylesheets>
<URL value="@../../css/employees.css"/>
<URL value="@../../css/table.css"/>
</stylesheets>
<children>
<HBox fx:id="topMenu" prefHeight="50.0" prefWidth="712.0" spacing="10.0" layoutX="14.0" layoutY="15">
<children>
<MFXButton fx:id="showBtn" onAction="#show" styleClass="employee-show" text="Show" disable="true">
<graphic>
<FontIcon iconLiteral="fas-user-tie"/>
</graphic>
</MFXButton>
<MFXButton fx:id="hireBtn" onAction="#hire" styleClass="employee-hire" text="Hire">
<graphic>
<FontIcon iconLiteral="fas-user-plus"/>
</graphic>
</MFXButton>
<MFXButton fx:id="updateBtn" onAction="#update" styleClass="employee-update" text="Update"
disable="true">
<graphic>
<FontIcon iconLiteral="fas-user-edit"/>
</graphic>
</MFXButton>
<MFXButton fx:id="terminateBtn" onAction="#terminate" styleClass="employee-terminate" text="Terminate"
disable="true">
<graphic>
<FontIcon iconLiteral="fas-user-alt-slash"/>
</graphic>
</MFXButton>
</children>
</HBox>
<MFXPaginatedTableView fx:id="table" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity"
prefHeight="700.0" prefWidth="772.0" layoutX="14.0" layoutY="85"/>
</children>
</AnchorPane>
and I setup my table like so:
private void setupTable()
{
MFXTableColumn<EmployeeEntity> firstNameColumn = new MFXTableColumn<>(
"Name", true, Comparator.comparing(EmployeeEntity::getFirstName));
MFXTableColumn<EmployeeEntity> lastNameColumn = new MFXTableColumn<>(
"Surname", true, Comparator.comparing(EmployeeEntity::getLastName));
MFXTableColumn<EmployeeEntity> departmentColumn = new MFXTableColumn<>(
"Department", true, Comparator.comparing(EmployeeEntity::getDepartment));
MFXTableColumn<EmployeeEntity> titleColumn = new MFXTableColumn<>(
"Title", true, Comparator.comparing(EmployeeEntity::getTitle));
MFXTableColumn<EmployeeEntity> salaryColumn = new MFXTableColumn<>(
"Salary", true, Comparator.comparing(EmployeeEntity::getSalary));
firstNameColumn.setRowCellFactory(employee -> new MFXTableRowCell<>(EmployeeEntity::getFirstName));
lastNameColumn.setRowCellFactory(employee -> new MFXTableRowCell<>(EmployeeEntity::getLastName));
departmentColumn.setRowCellFactory(employee -> new MFXTableRowCell<>(EmployeeEntity::getDepartment));
titleColumn.setRowCellFactory(employee -> new MFXTableRowCell<>(EmployeeEntity::getTitle));
salaryColumn.setRowCellFactory(employee -> new MFXTableRowCell<>(EmployeeEntity::getSalaryPretty) {{
setAlignment(Pos.CENTER_RIGHT);
}});
table.getTableColumns().addAll(
firstNameColumn,
lastNameColumn,
departmentColumn,
titleColumn,
salaryColumn
);
table.getFilters().addAll(
new StringFilter<>("Name", EmployeeEntity::getFirstName),
new StringFilter<>("Surname", EmployeeEntity::getLastName),
new StringFilter<>("Department", EmployeeEntity::getDepartment),
new StringFilter<>("Title", EmployeeEntity::getTitle),
new DoubleFilter<>("Salary", EmployeeEntity::getSalary)
);
table.setOnMouseClicked(e -> {
if (e.getButton().equals(MouseButton.PRIMARY))
{
showBtn.setDisable(!hasTableSelection());
updateBtn.setDisable(!hasTableSelection());
terminateBtn.setDisable(!hasTableSelection());
}
});
}
The initialize()
method of the controller has the following:
table.setRowsPerPage(19);
setupTable();
table.getSelectionModel().setAllowsMultipleSelection(false);
ObservableList<EmployeeEntity> employees = FXCollections.observableArrayList(
employeeRepository.findAll()
);
table.setItems(employees);
table.autosizeColumnsOnInitialization();
Normally, with a non MaterialFX table I would use table.getSelectionModel().selectedItemProperty().addListener(...)
to listen for any row selections, but with MFXPaginatedTableView
I don't see any way to accomplish it since selectedItemProperty()
isn't exposed by IMultipleSelectionModel
@panosru
Ah yes I finally see what's going on. So, from your code I can reproduce the issue, and I was ready to label this as a bug but then I realized what is the problem.
The MultipleSelectionModel uses a MapProperty and exposes getters and setters to access it. In some occasions the Map wrapped by the MapProperty is replaced with a new one (so that changes are "atomic") and this makes the getSelection()
method useless because the user doesn't know when this occurs. So, getSelection()
returns the current wrapped ObservableMap.
What you want to do instead is to attach the listener on the MapProperty so you have to do:
table.getSelectionModel().selectionProperty().addListener((MapChangeListener<? super Integer, ? super Device>) change -> {
// logic
});
Now, this is not a bug, but it's definitely an enhancement to make.
I still don't know what to do... maybe just remove the getSelection()
method or replace the MapProperty with an ObjectProperty<ObservableMpa>... I'll test the different approaches and figure out which is the best
@palexdev I was about to post a minimum code to reproduce since my previous post was a bit "hey here's my code, find me a solution" hahaha but thankfully you got the point :)
I think I have tried using selectionProperty()
, let me check again and I'll post back :)
UPDATE:
@palexdev yeah, using selectionProperty()
worked, I could swear that I tried it!! I really read docs and anything I can find before asking for support! I guess I might've missed it!
Thanks!
@panosru haha well your description of the issue was crystal clear and accompanied by some code so I didn't really need a MRE this time. You're welcome, thanks for using MaterialFX
P.S: I'll re-open this issue as a remainder for the enhancement
@palexdev well, based on what you have described, I guess maybe removing the getSelection()
method will reduce the confusion that others might also bump into. Currently, when I want to retrieve data from the table I use the following method:
private @Nullable EmployeeEntity getTableSelection()
{
if (hasTableSelection())
return table.getSelectionModel().getSelectedValues().get(0);
return null;
}
and hasTableSelection()
looks like so:
private boolean hasTableSelection()
{
return !table.getSelectionModel().getSelection().isEmpty();
}
As you can see, the only place where I'm using the getSelection()
method is there, but that could easily be replaced with getSelectedValues().isEmpty()
, so, yeah, I'm not seeing much value out of getSelection()
other than confusion, but, I might be missing something since I just started using your MaterialFX project.
@panosru Yeah I agree Heck you could even do:
deTable.getSelectionModel().selectionProperty().isEmpty();
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.