Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Table listener for row selection #186

Open
panosru opened this issue Apr 25, 2022 · 8 comments
Open

Table listener for row selection #186

panosru opened this issue Apr 25, 2022 · 8 comments
Labels
enhancement New feature or request

Comments

@panosru
Copy link

panosru commented Apr 25, 2022

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!

@Tech-Expert-Wizard
Copy link

Could you provide a minimal reproducible example?

@panosru
Copy link
Author

panosru commented Apr 26, 2022

@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

@palexdev
Copy link
Owner

@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... I'll test the different approaches and figure out which is the best

@palexdev palexdev added the enhancement New feature or request label Apr 26, 2022
@panosru
Copy link
Author

panosru commented Apr 26, 2022

@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 panosru closed this as completed Apr 26, 2022
@palexdev
Copy link
Owner

@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 palexdev reopened this Apr 26, 2022
@panosru
Copy link
Author

panosru commented Apr 26, 2022

@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.

@palexdev
Copy link
Owner

@panosru Yeah I agree
Heck you could even do:

deTable.getSelectionModel().selectionProperty().isEmpty();

@stale
Copy link

stale bot commented May 24, 2022

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.

@stale stale bot added the stale label May 24, 2022
@palexdev palexdev removed the stale label May 24, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants