JAVAFX - The hidden column's mysterious solution
Asked Answered
H

2

5

I have a TableView which shows all the data from a Derby table, except the "ID" of the items (I don't want the user to see it.)

The method is simple, I made an SQL statement to select all the attributes but I did not ask for the ID column. I got back a TableData and this variable is shown in the TableView with all of the records (It is a simple namelist).

I want to allow the user to delete from the table with a "delete button".

So first, there should be an "OnAction" method, (when the user clicks the delete button) when we gather the ACTUAL selected row's (if not null) ID, and we send a statement to the database to delete it, where the (HIDDEN) ID of the selected item can be found at the derby table.

Since it is a Namelist, and the user is allowed to make another record with the exactly same data, in the tableview the records can be clones, with no differences. (only the ID is uniqe, but the tableview does not contain the id-s, that makes it so hard).

So how could we delete the selected line without knowing it's ID? Or how can we know the ID when it is not shown in the table? (searching on the name is not working, since there can be several records with the same name)

What is the "hidden column's mysterious solution"? :)

Henni answered 14/3, 2015 at 22:1 Comment(0)
T
8

Your question has a simple solution. If you don't want to show the ID on the tableview, you don't have to. TableView binds with your class, in your case user, and not with its fields. When you add the values to the tableview, you can control which fields you want to show on it. Whether you show it on the tableview or not, you still have your complete user object with you (which still has the ID).

Now, there are multiple ways to do this. One of the way is to bind the id with the delete button that you want to show in each row. Whenever the delete button is pressed, delete the item from the DB and delete it from the tableview.

I have create an working example which shows how this work. I have hardcoded my values and instead of deleting it from the database, I am printing the ID value on the console.

In this example, the Person class can be considered equivalent to your user, each of which has an id and other attributes.

import javafx.application.Application;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;

public class TableViewSample extends Application {

    private TableView<Person> table = new TableView<Person>();
    private final ObservableList<Person> data =
            FXCollections.observableArrayList(
                    new Person(10, "Jacob", "Smith", "[email protected]"),
                    new Person(20, "Isabella", "Johnson", "[email protected]"),
                    new Person(30, "Ethan", "Williams", "[email protected]"),
                    new Person(40, "Emma", "Jones", "[email protected]"),
                    new Person(50, "Michael", "Brown", "[email protected]")
            );

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage stage) {
        Scene scene = new Scene(new Group());
        stage.setTitle("Table View Sample");
        stage.setWidth(600);
        stage.setHeight(500);

        final Label label = new Label("Address Book");
        label.setFont(new Font("Arial", 20));

        table.setEditable(true);

        TableColumn firstNameCol = new TableColumn("First Name");
        firstNameCol.setMinWidth(100);
        firstNameCol.setCellValueFactory(
                new PropertyValueFactory<Person, String>("firstName"));

        TableColumn lastNameCol = new TableColumn("Last Name");
        lastNameCol.setMinWidth(100);
        lastNameCol.setCellValueFactory(
                new PropertyValueFactory<Person, String>("lastName"));

        TableColumn emailCol = new TableColumn("Email");
        emailCol.setMinWidth(200);
        emailCol.setCellValueFactory(
                new PropertyValueFactory<Person, String>("email"));


        TableColumn deleteCol = new TableColumn("Delete");
        deleteCol.setMinWidth(100);
        deleteCol.setCellFactory(param -> new ButtonCell());
        deleteCol.setCellValueFactory(
                new PropertyValueFactory<Person, String>("id"));

        table.setItems(data);
        table.getColumns().addAll(firstNameCol, lastNameCol, emailCol, deleteCol);

        final VBox vbox = new VBox();
        vbox.setSpacing(5);
        vbox.setPadding(new Insets(10, 0, 0, 10));
        vbox.getChildren().addAll(label, table);

        ((Group) scene.getRoot()).getChildren().addAll(vbox);

        stage.setScene(scene);
        stage.show();
    }

    public static class Person {

        private final SimpleIntegerProperty id;
        private final SimpleStringProperty firstName;
        private final SimpleStringProperty lastName;
        private final SimpleStringProperty email;

        private Person(Integer id, String fName, String lName, String email) {
            this.id = new SimpleIntegerProperty(id);
            this.firstName = new SimpleStringProperty(fName);
            this.lastName = new SimpleStringProperty(lName);
            this.email = new SimpleStringProperty(email);
        }

        public int getId() {
            return id.get();
        }

        public void setId(int id) {
            this.id.set(id);
        }

        public String getFirstName() {
            return firstName.get();
        }

        public void setFirstName(String fName) {
            firstName.set(fName);
        }

        public String getLastName() {
            return lastName.get();
        }

        public void setLastName(String fName) {
            lastName.set(fName);
        }

        public String getEmail() {
            return email.get();
        }

        public void setEmail(String fName) {
            email.set(fName);
        }
    }

    private class ButtonCell extends TableCell<Person, Integer> {
        Image buttonDeleteImage = new Image("https://cdn1.iconfinder.com/data/icons/nuove/22x22/actions/fileclose.png");

        final Button cellDeleteButton = new Button("", new ImageView(buttonDeleteImage));

        ButtonCell() {
            cellDeleteButton.setOnAction(actionEvent -> {
                System.out.println("Deleted Id : " + getItem());// Make a DB call and delete the person with ID
                getTableView().getItems().remove(getIndex());
            });
        }

        @Override
        protected void updateItem(Integer t, boolean empty) {
            super.updateItem(t, empty);
            if (!empty) {
                setGraphic(cellDeleteButton);
            } else {
                setGraphic(null);
            }
        }
    }
}

To do it without binding ID

You need :

deleteCol.setCellValueFactory(p -> {
        return new ReadOnlyObjectWrapper<Person>((Person)p.getValue());
});

The custom ButtonCell should extend TableCell<Person, Person>

The logic to delete item becomes :

System.out.println("Deleted ID : " +
                        getItem().getId());// Make a DB call and delete the person with ID
getTableView().getItems().remove(getIndex());

Complete Example:

public class TableViewSample extends Application {

    private TableView<Person> table = new TableView<Person>();
    private final ObservableList<Person> data =
            FXCollections.observableArrayList(
                    new Person(10, "Jacob", "Smith", "[email protected]"),
                    new Person(20, "Isabella", "Johnson", "[email protected]"),
                    new Person(30, "Ethan", "Williams", "[email protected]"),
                    new Person(40, "Emma", "Jones", "[email protected]"),
                    new Person(50, "Michael", "Brown", "[email protected]")
            );

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage stage) {
        Scene scene = new Scene(new Group());
        stage.setTitle("Table View Sample");
        stage.setWidth(600);
        stage.setHeight(500);

        final Label label = new Label("Address Book");
        label.setFont(new Font("Arial", 20));

        table.setEditable(true);

        TableColumn firstNameCol = new TableColumn("First Name");
        firstNameCol.setMinWidth(100);
        firstNameCol.setCellValueFactory(
                new PropertyValueFactory<Person, String>("firstName"));

        TableColumn lastNameCol = new TableColumn("Last Name");
        lastNameCol.setMinWidth(100);
        lastNameCol.setCellValueFactory(
                new PropertyValueFactory<Person, String>("lastName"));

        TableColumn emailCol = new TableColumn("Email");
        emailCol.setMinWidth(200);
        emailCol.setCellValueFactory(
                new PropertyValueFactory<Person, String>("email"));


        TableColumn deleteCol = new TableColumn("Delete");
        deleteCol.setMinWidth(100);
        deleteCol.setCellFactory(param -> new ButtonCell());
        deleteCol.setCellValueFactory(p -> {
            return new ReadOnlyObjectWrapper<Person>((Person)p.getValue());
        });

        table.setItems(data);
        table.getColumns().addAll(firstNameCol, lastNameCol, emailCol, deleteCol);

        final VBox vbox = new VBox();
        vbox.setSpacing(5);
        vbox.setPadding(new Insets(10, 0, 0, 10));
        vbox.getChildren().addAll(label, table);

        ((Group) scene.getRoot()).getChildren().addAll(vbox);

        stage.setScene(scene);
        stage.show();
    }

    public static class Person {

        private final SimpleIntegerProperty id;
        private final SimpleStringProperty firstName;
        private final SimpleStringProperty lastName;
        private final SimpleStringProperty email;

        private Person(Integer id, String fName, String lName, String email) {
            this.id = new SimpleIntegerProperty(id);
            this.firstName = new SimpleStringProperty(fName);
            this.lastName = new SimpleStringProperty(lName);
            this.email = new SimpleStringProperty(email);
        }

        public int getId() {
            return id.get();
        }

        public void setId(int id) {
            this.id.set(id);
        }

        public String getFirstName() {
            return firstName.get();
        }

        public void setFirstName(String fName) {
            firstName.set(fName);
        }

        public String getLastName() {
            return lastName.get();
        }

        public void setLastName(String fName) {
            lastName.set(fName);
        }

        public String getEmail() {
            return email.get();
        }

        public void setEmail(String fName) {
            email.set(fName);
        }
    }

    private class ButtonCell extends TableCell<Person, Person> {
        Image buttonDeleteImage = new Image("https://cdn1.iconfinder.com/data/icons/nuove/22x22/actions/fileclose.png");

        final Button cellDeleteButton = new Button("", new ImageView(buttonDeleteImage));

        ButtonCell() {
            cellDeleteButton.setOnAction(actionEvent -> {
                System.out.println("Deleted ID : " +
                        getItem().getId());// Make a DB call and delete the person with ID
                getTableView().getItems().remove(getIndex());
            });
        }

        @Override
        protected void updateItem(Person t, boolean empty) {
            super.updateItem(t, empty);
            if (!empty) {
                setGraphic(cellDeleteButton);
            } else {
                setGraphic(null);
            }
        }
    }
}
Taw answered 15/3, 2015 at 9:31 Comment(4)
Note that instead of getTableView().getItems().get(getIndex()) you can just do getItem().Tenpins
@Tenpins In the first example I have used getItem(), but for TableCell<Person, Person>, getItem() threw NullPointerException, not sure why.Taw
Oh, ok, you don't have a cellValueFactory. So the item never gets set. You would also need deleteCol.setCellValueFactory(cellData -> new ReadOnlyObjectProperty<Person>(cellData.getValue()));Tenpins
Thanks, I used ReadOnlyObjectWrapper instead of ReadOnlyObjectProperty and everything works :)Taw
H
2

I have found an easier way, where the user can list anything, and he decides what to show and what to hide. This solution also works if we don't know what data will come to us, but We are sure that will be "ID" column we do not wan't to show in attention:

        if (*ColumNameWeWantToHide*.equals(String.valueOf(columnName))) {
            col.prefWidthProperty().bind(*TableViewName*.widthProperty().divide(100));
        } 

It makes us also able to work with ID numbers simply, but also we are abla to watch the ID if we want, but on default we do not see them. That is a good mixture of useability and easy coding.

Henni answered 23/3, 2015 at 14:30 Comment(1)
No, i just doen't agree, this is bad coding style. Your are using a tweek to hide the column. What if you rename the column later on? You have to search where you also used the old name and change it. It is very likely that you mess up your code. Use ItachiUchiha solution! It is much clearer and easy to refactor later on.Asphaltite

© 2022 - 2024 — McMap. All rights reserved.