javafx-8observablelistjavafx-tableview

JavaFX 8 TableView not populating from ObservableList (no FXML)


I believe I've searched all of the similar questions, but still am not seeing my issue anywhere. I am populating an ObservableList from a database which is succeeding per my Console output. I have a multiple controller setup for a school project to create a scheduler app. I have a root controller which functions as the borderPane, two functional controllers for Appointments and Customers which are all Singleton, and a shared DataView controller which is not to allow each view to instantiate its' own DataView. I've implemented toString on each of the controllers to spit out what the values and/or object Ids of each element are and everything seems to line up. I can't for the life of me figure out why the ListView or TableView aren't outputting the bound data. Here's the view data that I'm trying to bind to both ListView and TableView

public class AppointmentView implements IAppointmentView {
    private final ZonedDateTime createdDate;
    private final String createdBy;
    // Interface needs these components
    private ReadOnlyStringProperty title;
    private ReadOnlyStringProperty description;
    private ReadOnlyStringProperty location;
    private ReadOnlyStringProperty contact;
    private ReadOnlyStringProperty url;
    private ReadOnlyStringProperty customerName;
    private ReadOnlyObjectProperty<ZonedDateTime> start;
    private ReadOnlyObjectProperty<ZonedDateTime> end;
    private ReadOnlyProperty<ZonedDateTime> lastUpdated;

    /***
     *
     * @param title
     * @param description
     * @param location
     * @param contact
     * @param url
     * @param customerName
     * @param start
     * @param end
     * @param createDate
     * @param createdBy
     * @param lastUpdate
     */
    public AppointmentView(String title, String description, String location, String contact, String url, String customerName, Timestamp start, Timestamp end, Timestamp createDate, String createdBy, Timestamp lastUpdate) {
        this.title = new SimpleStringProperty(title);
        this.description = new SimpleStringProperty(description);
        this.location = new SimpleStringProperty(location);
        this.contact = new SimpleStringProperty(contact);
        this.customerName = new SimpleStringProperty(customerName);
        this.start = new SimpleObjectProperty<>(ZonedDateTime.ofInstant(start.toInstant(), ZoneId.systemDefault()));
        this.end = new SimpleObjectProperty<>(ZonedDateTime.ofInstant(end.toInstant(), ZoneId.systemDefault()));
        this.lastUpdated = new SimpleObjectProperty<>(ZonedDateTime.ofInstant(lastUpdate.toInstant(), ZoneId.systemDefault()));
        this.url = new SimpleStringProperty(url);
        this.createdDate = ZonedDateTime.ofInstant(createDate.toInstant(), ZoneId.systemDefault());
        this.createdBy = createdBy;

    }

    public String getTitle() {
        return title.getValue();
    }

    ReadOnlyStringProperty titleProperty() {
        return title;
    }

    public String getDescription() {
        return description.getValue();
    }

    ReadOnlyStringProperty descriptionProperty() {
        return description;
    }

    public String getLocation() {
        return location.getValue();
    }

    ReadOnlyStringProperty locationProperty() {
        return location;
    }

    public String getContact() {
        return contact.getValue();
    }

    ReadOnlyStringProperty contactProperty() {
        return contact;
    }

    public String getUrl() {
        return url.getValueSafe();
    }

    ReadOnlyStringProperty urlProperty() {
        return url;
    }

    public String getCustomerName() {
        return customerName.getValue();
    }

    ReadOnlyStringProperty customerNameProperty() {
        return customerName;
    }

    public ZonedDateTime getStart() {
        return ZonedDateTime.ofInstant(start.getValue().toInstant(), ZoneId.systemDefault());
    }

    ReadOnlyProperty<ZonedDateTime> startProperty() {
        return start;
    }

    public ZonedDateTime getEnd() {
        return ZonedDateTime.ofInstant(end.getValue().toInstant(), ZoneId.systemDefault());
    }

    ReadOnlyProperty<ZonedDateTime> endProperty() {
        return end;
    }

    public LocalDate getCreateDate() {
        return createdDate.toLocalDate();
    }

    public String getCreatedBy() {
        return createdBy;
    }

    public ZonedDateTime getLastUpdate() {
        return ZonedDateTime.ofInstant(lastUpdated.getValue().toInstant(), ZoneId.systemDefault());
    }

    ReadOnlyProperty<ZonedDateTime> lastUpdatedProperty() {
        return lastUpdated;
    }

    @Override
    public String toString() {
        final StringBuffer sb = new StringBuffer("AppointmentView{");
        sb.append("createdDate=").append(createdDate);
        sb.append(", createdBy='").append(createdBy).append('\'');
        sb.append(", titleProperty=").append(title);
        sb.append(", descriptionProperty=").append(description);
        sb.append(", locationProperty=").append(location);
        sb.append(", contactProperty=").append(contact);
        sb.append(", urlProperty=").append(url);
        sb.append(", customerName=").append(customerName);
        sb.append(", startProperty=").append(start);
        sb.append(", endProperty=").append(end);
        sb.append(", lastUpdated=").append(lastUpdated);
        sb.append('}');
        return sb.toString();
    }
}

Here's the AppViewController:

public class AppViewController extends BorderPane {


    private BorderPane rootPane;
    private MenuBar menuBar;
    private Menu fileMenu;
    private Menu editMenu;
    private Menu reportMenu;
    private Menu helpMenu;
    private MenuItem closeMenuItem;
    private MenuItem copyMenuItem;
    private MenuItem monthlyAppointmentReportMenuItem;
    private MenuItem consultantScheduleMenuItem;
    private MenuItem customersByCountryMenuItem;
    private MenuItem aboutMenuItem;
    private VBox vbAppView;
    private TabPane tpAppPane;
    private Tab tabCustomers;
    private ScrollPane spCustomerEditor;
    private Tab tabAppointments;
    private ScrollPane spAppointmentEditor;
    private MainApp mainApp;
    private static AppViewController instance;
    private static AppointmentViewController appointmentViewController = AppointmentViewController.getInstance();
    private static CustomerViewController customerViewController = CustomerViewController.getInstance();
    private static DataViewController dataViewController;

    private AppViewController() {
        initialize();
    }

    public static AppViewController getInstance() {
        if (instance == null) {
            new AppViewController();
        }
        return instance;
    }

    /**
     * Called to initialize a controller after its root element has been
     * completely processed.
     **/

    public void initialize() {
        instance = this;
        this.rootPane = new BorderPane();
        this.menuBar = new MenuBar();
        this.fileMenu = new Menu("_File");
        this.editMenu = new Menu("_Edit");
        this.reportMenu = new Menu("_Report");
        this.helpMenu = new Menu("_Help");
        this.closeMenuItem = new MenuItem("Close");
        this.copyMenuItem = new MenuItem("Copy");
        this.monthlyAppointmentReportMenuItem = new MenuItem("Monthly Appointment Report");
        this.consultantScheduleMenuItem = new MenuItem("Consultant Schedule Report");
        this.customersByCountryMenuItem = new MenuItem("Customers by Country Report");
        this.aboutMenuItem = new MenuItem("About");
        this.vbAppView = new VBox();
        this.tpAppPane = new TabPane();
        this.tabCustomers = new Tab("Customers");
        this.tabCustomers.setClosable(false);
        this.spCustomerEditor = new ScrollPane();
        this.tabAppointments = new Tab("Appointments");
        this.tabAppointments.setClosable(false);
        this.spAppointmentEditor = new ScrollPane();

        // populate menus and menuBar and add them to top pane

        this.fileMenu.getItems().setAll(closeMenuItem);
        this.fileMenu.setMnemonicParsing(true);
        this.editMenu.getItems().setAll(copyMenuItem);
        this.editMenu.setMnemonicParsing(true);
        this.reportMenu
                .getItems()
                .setAll(monthlyAppointmentReportMenuItem, consultantScheduleMenuItem, customersByCountryMenuItem);
        this.reportMenu.setMnemonicParsing(true);
        this.helpMenu.getItems().setAll(aboutMenuItem);
        this.helpMenu.setMnemonicParsing(true);

        this.menuBar.getMenus().addAll(fileMenu, editMenu, reportMenu, helpMenu);
        this.rootPane.setTop(menuBar);

        // populate scroll panes with included views
        this.spAppointmentEditor.setContent(getAppointmentView());
        this.spCustomerEditor.setContent(getCustomerView());

        // populate tab panes and controllers and add them to the center pane
        this.tabAppointments.setContent(spAppointmentEditor);
        this.tabCustomers.setContent(spCustomerEditor);
        this.tpAppPane.getTabs().addAll(tabAppointments, tabCustomers);

        vbAppView.getChildren().addAll(tpAppPane);
        this.rootPane.setCenter(vbAppView);

        // add data view to bottom pane
        this.rootPane.setBottom(AppointmentViewController.getDataView());

        setupEventHandlers(this);
    }

    private void setupEventHandlers(AppViewController appViewController) {

        this.tabCustomers.setOnSelectionChanged((event -> {
            if (tabCustomers.isSelected()) {
                // Change to Customer View
                setCustomerView();
            } else {
                setAppointmentView();
            }
        }));

        this.tabAppointments.setOnSelectionChanged(event -> {
            if (tabAppointments.isSelected()) {
                setAppointmentView();
            } else {
                setCustomerView();
            }
        });

        this.closeMenuItem.setOnAction(event -> quitApp());

        this.tpAppPane.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
            if(newValue.getText().toLowerCase().equals("Customer".toLowerCase())){
                setCustomerView();
            }

            if (newValue.getText().toLowerCase().equals("Appointments".toLowerCase())){
                setAppointmentView();
            }
        });
    }

    private void setCustomerView() {
        CustomerViewController customerViewController = CustomerViewController.getInstance();
        setDataView(customerViewController.getDataViewController().tabPane);
        customerViewController.getGpCustomerEditor().getChildren().filtered(node -> toggleTextFields(node, true));

    }

    private void setAppointmentView() {
        AppointmentViewController controller = AppointmentViewController.getInstance();
        setDataView(controller.getDataViewController().tabPane);
        controller.getGpAppointmentEditor().getChildren().filtered(node -> toggleTextFields(node, true));
    }

    /***
     * Sets whether text fields are enabled or disabled
     * true = disable Text Fields
     * false = enable Text Fields
     * @param node the child nodes of the editor
     * @param disabled whether to disable or enable the text fields
     * @return whether node was affected or not
     */
    public static boolean toggleTextFields(Node node, boolean disabled) {
        if (node instanceof TextField) {
            ((TextField) node).setEditable(!disabled);
            node.setDisable(disabled);
            return true;
        }
        return false;
    }

    public void setMainApp(MainApp mainApp) {
        this.mainApp = mainApp;
    }

    private void quitApp() {
        Platform.exit();
    }

    public Parent getBorderPane() {
        return rootPane;
    }

    public TabPane getTpAppPane() {
        return tpAppPane;
    }

    public Tab getTabCustomers() {
        return tabCustomers;
    }

    public Tab getTabAppointments() {
        return tabAppointments;
    }

    @Override
    public String toString() {
        final StringBuffer sb = new StringBuffer("AppViewController{");
        sb.append("\nrootPane=").append(rootPane);
        sb.append(",\n menuBar=").append(menuBar);
        sb.append(",\n fileMenu=").append(fileMenu);
        sb.append(",\n editMenu=").append(editMenu);
        sb.append(",\n reportMenu=").append(reportMenu);
        sb.append(",\n helpMenu=").append(helpMenu);
        sb.append(",\n closeMenuItem=").append(closeMenuItem);
        sb.append(",\n copyMenuItem=").append(copyMenuItem);
        sb.append(",\n monthlyAppointmentReportMenuItem=").append(monthlyAppointmentReportMenuItem);
        sb.append(",\n consultantScheduleMenuItem=").append(consultantScheduleMenuItem);
        sb.append(",\n customersByCountryMenuItem=").append(customersByCountryMenuItem);
        sb.append(",\n aboutMenuItem=").append(aboutMenuItem);
        sb.append(",\n vbAppView=").append(vbAppView);
        sb.append(",\n tpAppPane=").append(tpAppPane);
        sb.append(",\n tabCustomers=").append(tabCustomers);
        sb.append(",\n spCustomerEditor=").append(spCustomerEditor);
        sb.append(",\n tabAppointments=").append(tabAppointments);
        sb.append(",\n spAppointmentEditor=").append(spAppointmentEditor);
        sb.append(",\n dataViewController=").append(dataViewController);
        sb.append("\n}");
        return sb.toString();
    }

    public void setDataViewController(DataViewController dataViewController) {
        this.dataViewController = dataViewController;
    }

    public DataViewController getDataViewController() {
        return dataViewController;
    }
}

And the AppointmentView:

private TableView<AppointmentView> tvAppointments = new TableView<>();
private TableColumn<AppointmentView, String> tcTitle = new TableColumn<>("Title");
private TableColumn<AppointmentView, String> tcDescription = new TableColumn<>("Description");
private TableColumn<AppointmentView, String> tcLocation = new TableColumn<>("Location");
private TableColumn<AppointmentView, String> tcContact = new TableColumn<>("Contact");
private TableColumn<AppointmentView, String> tcUrl = new TableColumn<>("URL");
private TableColumn<AppointmentView, String> tcCustomerName = new TableColumn<>("Customer Name");
private TableColumn<AppointmentView, ZonedDateTime> tcStart = new TableColumn<>("Start Time");
private TableColumn<AppointmentView, ZonedDateTime> tcEnd = new TableColumn<>("End Time");
private TableColumn<AppointmentView, ZonedDateTime> tcCreateDate = new TableColumn<>("Created Date");
private TableColumn<AppointmentView, String> tcCreatedBy = new TableColumn<>("Created By");
private TableColumn<AppointmentView, Timestamp> tcLastUpdate = new TableColumn<>("Last Updated");
---- snip ----
    this.tcTitle.setCellValueFactory(new PropertyValueFactory<>("title"));
    this.tcTitle.setVisible(true);
    this.tcTitle.setMinWidth(50);

    this.tcCustomerName.setCellValueFactory(new PropertyValueFactory<>("customerName"));
    this.tcCustomerName.setVisible(true);
    this.tcCustomerName.setMinWidth(40);

    this.tcDescription.setCellValueFactory(new PropertyValueFactory<>("description"));
    this.tcDescription.setVisible(true);
    this.tcDescription.setMinWidth(100);

    this.tcLocation.setCellValueFactory(new PropertyValueFactory<>("location"));
    this.tcLocation.setVisible(true);
    this.tcLocation.setMinWidth(40);

    this.tcContact.setCellValueFactory(new PropertyValueFactory<>("contact"));
    this.tcContact.setVisible(true);
    this.tcContact.setMinWidth(40);

    this.tcUrl.setCellValueFactory(new PropertyValueFactory<>("url".toUpperCase()));
    this.tcUrl.setVisible(true);
    this.tcUrl.setMinWidth(40);

    this.tcStart.setCellValueFactory(new PropertyValueFactory<>("start"));
    this.tcStart.setVisible(true);
    this.tcStart.setMinWidth(40);

    this.tcEnd.setCellValueFactory(new PropertyValueFactory<>("end"));
    this.tcEnd.setVisible(true);
    this.tcEnd.setMinWidth(40);

    this.tcCreateDate.setCellValueFactory(new PropertyValueFactory<>("createDate"));
    this.tcCreateDate.setVisible(true);
    this.tcCreateDate.setMinWidth(40);

    this.tcCreatedBy.setCellValueFactory(new PropertyValueFactory<>("createdBy"));
    this.tcCreatedBy.setVisible(true);
    this.tcCreatedBy.setMinWidth(40);

    this.tcLastUpdate.setCellValueFactory(new PropertyValueFactory<>("lastUpdate"));
    this.tcLastUpdate.setVisible(true);
    this.tcLastUpdate.setMinWidth(40);

    this.tvAppointments = new TableView<>();
    this.tvAppointments.setItems(appointmentViews);
    this.tvAppointments.getColumns().addAll(
            tcTitle,
            tcDescription,
            tcLocation,
            tcContact,
            tcUrl,
            tcCustomerName,
            tcStart,
            tcEnd,
            tcCreateDate,
            tcCreatedBy,
            tcLastUpdate);


    this.lvListView = new ListView<>();
    this.lvListView.setItems(appointmentViews);

    this.dataViewController.setTableView(this.tvAppointments);
    this.dataViewController.setLblListView(new Label("Appointment List"));
    this.dataViewController.setListView(this.lvListView);
    this.dataViewController.setLblTableView(new Label("Appointments"));

And the base DataViewController that I'm trying to manipulate:

public class DataViewController extends TabPane {

//    private static DataViewController instance;
    public TabPane tabPane;
    private Tab tabTableView;
    private Tab tabListView;
    private VBox vbListView;
    private VBox vbTableView;
    private Label lblListView;
    private Label lblTableView;
    protected ScrollPane spListView;
    protected ScrollPane spTableView;
    private ListView<?> listView;
    private TableView<?> tableView;
    private MainApp mainApp;

    public DataViewController() {
        initialize();
    }

/*    public static DataViewController getInstance(){
        if(instance == null){
            new DataViewController();
        }
        return instance;
    }*/

    /**
     * Called to initialize a controller after its root element has been
     * completely processed.
     *
     */
    public void initialize() {
//        this.instance = this;
        this.tabPane = new TabPane();
        this.tabPane.setPrefHeight(250.0);
        this.tabPane.setMaxHeight(400.0);
        this.tabPane.setMaxWidth(Integer.MAX_VALUE);
        this.tabTableView = new Tab("Table View");
        this.tabTableView.setClosable(false);
        this.tabListView = new Tab("List View");
        this.tabListView.setClosable(false);
        this.spListView = new ScrollPane();
        this.spTableView = new ScrollPane();
        this.lblListView = new Label("List View");
        this.lblTableView = new Label("Table View");
        this.vbListView = new VBox();
        this.vbTableView = new VBox();
        this.listView = new ListView<>();
        this.tableView = new TableView<>();

        this.vbListView.getChildren().setAll(this.lblListView, this.listView);
        this.spListView.setContent(this.listView);
        this.tabListView.setContent(this.spListView);


        this.vbTableView.getChildren().setAll(this.lblTableView, this.spTableView);
        this.spTableView.setContent(this.tableView);
        this.tabTableView.setContent(vbTableView);

        this.tabPane.getTabs().setAll(tabListView, tabTableView);
    }

    public Label getLblListView() {
        return lblListView;
    }

    public void setLblListView(Label lblListView) {
        this.lblListView = lblListView;
    }

    public Label getLblTableView() {
        return lblTableView;
    }

    public void setLblTableView(Label lblTableView) {
        this.lblTableView = lblTableView;
    }

    public ListView<?> getListView() {
        return listView;
    }

    public void setListView(ListView<?> listView) {
        this.listView = listView;
    }

    public TableView<?> getTableView() {
        return tableView;
    }

    public void setTableView(TableView<?> tableView) {
        this.tableView = tableView;
    }

    public void setMainApp(MainApp mainApp){
        this.mainApp = mainApp;
    }

/*    public static DataViewController getInstance() {
        if (instance == null){
            instance = new DataViewController();
        }
        return instance;
    }*/

    @Override
    public String toString() {
        return new StringBuilder()
                .append("DataViewController{")
                .append("\ntabPane=")
                .append(tabPane)
                .append(", \ntabTableView=")
                .append(tabTableView)
                .append(", \ntabListView=")
                .append(tabListView)
                .append(", \nvbListView=")
                .append(vbListView)
                .append(", \nvbTableView=")
                .append(vbTableView)
                .append(", \nlblListView=")
                .append(lblListView.getText())
                .append(", \nlblTableView=")
                .append(lblTableView.getText())
                .append(", \nspListView=")
                .append(spListView)
                .append(", \nspTableView=")
                .append(spTableView)
                .append(", \nlistView=")
                .append(listView.getItems())
                .append(", \ntableView=")
                .append(tableView.getColumns())
                .append("\n}")
                .toString();
    }
}

As mentioned, I put in a few toString calls and implementations and I keep seeing the objects in DataViewController per this:

listView=[AppointmentView{createdDate=2017-09-02T00:00-07:00[America/Los_Angeles], createdBy='test1', titleProperty=StringProperty [value: Meet with Amari], descriptionProperty=StringProperty [value: Meeting WR of the Raiders], locationProperty=StringProperty [value: Raiders HQ, Alameda], contactProperty=StringProperty [value: Jack DelRio], urlProperty=StringProperty [value: raiders.com], customerName=StringProperty [value: Amari Cooper], startProperty=ObjectProperty [value: 2017-09-04T16:00-07:00[America/Los_Angeles]], endProperty=ObjectProperty [value: 2017-09-04T16:15-07:00[America/Los_Angeles]], lastUpdated=ObjectProperty [value: 2017-09-02T23:07-07:00[America/Los_Angeles]]}, AppointmentView{createdDate=2017-09-02T00:00-07:00[America/Los_Angeles], createdBy='test1', titleProperty=StringProperty [value: Meet with Amari], descriptionProperty=StringProperty [value: Meeting WR of the Raiders], locationProperty=StringProperty [value: Raiders HQ, Alameda], contactProperty=StringProperty [value: Jack DelRio], urlProperty=StringProperty [value: raiders.com], customerName=StringProperty [value: Amari Cooper], startProperty=ObjectProperty [value: 2017-09-04T16:00-07:00[America/Los_Angeles]], endProperty=ObjectProperty [value: 2017-09-04T16:15-07:00[America/Los_Angeles]], lastUpdated=ObjectProperty [value: 2017-09-02T23:07-07:00[America/Los_Angeles]]}], 
tableView=[javafx.scene.control.TableColumn@6167e82, javafx.scene.control.TableColumn@50658769, javafx.scene.control.TableColumn@f65aae1, javafx.scene.control.TableColumn@2d5ab3ab, javafx.scene.control.TableColumn@180d2aec, javafx.scene.control.TableColumn@64afb84b, javafx.scene.control.TableColumn@463f349d, javafx.scene.control.TableColumn@3e80101a, javafx.scene.control.TableColumn@4fab076c, javafx.scene.control.TableColumn@565f8332, javafx.scene.control.TableColumn@697bdeb8]

And here's the MainApp method that's calling it:

private void initLayout() {
//        rootPane = new BorderPane();



    appointmentViewController = AppointmentViewController.getInstance();
    appointmentView = appointmentViewController.apAppointmentView;
    appointmentViewController.setMainApp(this);

    dataViewController = appointmentViewController.getDataViewController();
    dataView = dataViewController.tabPane;
    dataViewController.setMainApp(this);

    customerViewController = CustomerViewController.getInstance();
    customerView = customerViewController.apCustomerView;
    customerViewController.setMainApp(this);

    appViewController = AppViewController.getInstance();
    appView = appViewController.getBorderPane();
    appViewController.setMainApp(this);

    appViewController.setDataViewController(appointmentViewController.getDataViewController());

    System.out.println(this.dataViewController);
    System.out.println(this.dataView);
    System.out.println(appointmentViewController.toString());
    System.out.println(customerViewController.toString());
    System.out.println(appViewController.toString());

    rootPane = (BorderPane) appView;


    scene = new Scene(rootPane);
    scene.getStylesheets().add("/styles/Styles.css");

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

I've setup the CellValueFactories,etc. What am I missing here?

This is how the UI is shown:
Image of Application View


Solution

  • In DataViewController the scene structure is initialized from the constructor. Using any of the setters in this class modifies the fields, it does not modify the scene in any way leaving the old empty ListView in the scene but printing the new one containing items from the toString method.