Showing data in a table
IA tooling, not an exam topic. These are JavaFX patterns to adapt for your IA. Nothing here is examinable, and your IA must be your own work.
A TableView shows a list of objects as rows and columns. It ties straight into the OOP you already know: each row is one object of a small model class, and each column reads one of that class’s getters. This page builds the table, fills it, reads the selected row, keeps it current, and filters it live.
Display rows in a scrollable table
What this does: shows your objects as a table with named columns. The table scrolls on its own once there are more rows than fit.
When you would use it in your IA: any list the client needs to see at a glance: products, bookings, students, transactions. The table is the usual home screen for a data-driven app.
The pattern:
First, a small model class. This is ordinary OOP: private fields with getters.
public class Item {
private String name;
private int quantity;
public Item(String name, int quantity) {
this.name = name;
this.quantity = quantity;
}
public String getName() {
return name;
}
public int getQuantity() {
return quantity;
}
}
Controller imports:
import javafx.fxml.FXML;
import javafx.scene.control.TableView;
import javafx.scene.control.TableColumn;
import javafx.scene.control.cell.PropertyValueFactory;
Controller setup:
@FXML private TableView<Item> tblItems;
@FXML private TableColumn<Item, String> colName;
@FXML private TableColumn<Item, Integer> colQuantity;
@FXML
public void initialize() {
// The string must match the getter: getName() -> "name", getQuantity() -> "quantity".
colName.setCellValueFactory(new PropertyValueFactory<>("name"));
colQuantity.setCellValueFactory(new PropertyValueFactory<>("quantity"));
}
FXML (the table and its columns, with matching fx:ids):
<TableView fx:id="tblItems">
<columns>
<TableColumn fx:id="colName" text="Name" />
<TableColumn fx:id="colQuantity" text="Quantity" />
</columns>
</TableView>
Make it your own:
- Replace
Itemwith your own model class, and give it one getter per column you want to show. - For each column, the
PropertyValueFactorystring is the field name as it appears in the getter:getPrice()becomes"price". - The
<Item>inTableView<Item>is a type parameter that says “this table holds Item objects”. It works likeArrayList<Item>.
Watch out for:
- If a column comes up blank, the most common cause is a
PropertyValueFactorystring that does not match the getter name. Check the spelling and capitalisation. - The table scrolls automatically, so you do not need to add a scroll bar yourself.
Mix with: Fill a table from your data, Find out which row the user clicked.
Fill a table from your data
What this does: puts your objects into the table by wrapping them in an ObservableList, the special list a TableView watches.
When you would use it in your IA: loading saved records at startup, or showing the contents of an ArrayList you already built elsewhere in the program.
The pattern:
Imports:
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
Controller:
private final ObservableList<Item> items = FXCollections.observableArrayList();
@FXML
public void initialize() {
// ... column setup from the previous recipe ...
items.add(new Item("Apples", 12));
items.add(new Item("Bread", 3));
tblItems.setItems(items); // hand the list to the table once
}
If you already have a plain ArrayList, wrap it in one line:
ObservableList<Item> items = FXCollections.observableArrayList(myArrayList);
tblItems.setItems(items);
Make it your own:
- Build the
Itemobjects from your own data: typed-in values, a loaded file, or a database query. - Keep one
ObservableListas the table’s data and add to it, rather than building a new list each time.
Watch out for:
setItems(...)only needs to be called once. After that, changing the list updates the table for you (see the next recipe).- Use
FXCollections.observableArrayList(...), not a plainnew ArrayList<>(), for the list the table watches. A plain list will display but will not update live.
Mix with: Keep the table up to date, Save and load text.
Find out which row the user clicked
What this does: reads the object for the row the user has selected, so you can edit it, delete it, or show its details.
When you would use it in your IA: an Edit or Delete button that acts on “the row I clicked”; showing a detail panel for the selected record.
The pattern:
@FXML
private void handleEdit() {
Item selected = tblItems.getSelectionModel().getSelectedItem();
if (selected == null) {
// nothing is selected; tell the user and stop
return;
}
// Use selected, for example show its details or open an edit screen.
openEditScreen(selected);
}
Make it your own:
getSelectedItem()returns the full object for that row, so you have every field through its getters.- Change
openEditScreen(selected)to whatever you want to do with the chosen row.
Watch out for:
- It returns
nullwhen nothing is selected, so always check fornullbefore using the result. Skipping this is the usual cause of a crash on a Delete button. - By default the user can select one row at a time, which is what most IA apps want.
Mix with: Ask before doing something destructive, Pass information between screens.
Keep the table up to date
What this does: refreshes the table by changing the ObservableList behind it. Because the table watches that list, the display follows along on its own.
When you would use it in your IA: adding a new record, deleting the selected one, or clearing everything. The table should reflect the change straight away.
The pattern:
// Add a row: it appears immediately.
items.add(new Item("Milk", 2));
// Remove the selected row.
Item selected = tblItems.getSelectionModel().getSelectedItem();
if (selected != null) {
items.remove(selected);
}
// Clear the whole table.
items.clear();
Make it your own:
- Add, remove, and clear on the same
ObservableListyou handed tosetItems(...). You do not callsetItemsagain.
Watch out for:
- This works because you are changing the list itself. If you build a brand-new list, you must call
setItems(...)again so the table knows about it. - Changing a value inside an existing object (for example raising a quantity in place) does not always refresh the cell, because the table read that value once. Call
tblItems.refresh()after such an edit, or remove and re-add the object.
Mix with: Fill a table from your data, Filter the table as the user types, Save and load text.
Filter the table as the user types
What this does: shows only the rows that match what the user types in a search box, updating with every keystroke. It works the same way for a plain list.
When you would use it in your IA: a search box above a long table so the client can find one record quickly without scrolling.
The pattern: keep the full data in one list, and feed the table a shorter list of the rows that match. The match here is a hand-written loop, built on the searching and looping you already know.
Imports:
import javafx.fxml.FXML;
import javafx.scene.control.TextField;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
Controller:
@FXML private TextField txtSearch;
// allItems is the full data; the table shows whichever rows match.
private final ObservableList<Item> allItems = FXCollections.observableArrayList();
@FXML
public void initialize() {
// ... column setup, then fill allItems ...
tblItems.setItems(allItems);
// Re-filter every time the search text changes.
txtSearch.textProperty().addListener((obs, oldText, newText) -> applyFilter(newText));
}
private void applyFilter(String query) {
String needle = query.toLowerCase().trim();
ObservableList<Item> matches = FXCollections.observableArrayList();
for (Item item : allItems) {
if (item.getName().toLowerCase().contains(needle)) {
matches.add(item);
}
}
tblItems.setItems(matches);
}
Make it your own:
- Change the
ifcondition to match the field your users search by, or to check several fields at once. containsdoes a “anywhere in the text” match. UsestartsWithif you want prefix matching instead.toLowerCase()on both sides makes the search ignore capitalisation.
Watch out for:
- Keep
allItemsas the untouched master list and only ever buildmatchesfrom it. If you filter the master list in place, the rows you hid are gone for good. - The listener uses a lambda (the
(obs, oldText, newText) -> ...part), which is outside the exam-style Java subset but is the normal way to watch a text field. - JavaFX also offers a built-in
FilteredListthat does this without the manual loop. The loop is shown first because it keeps the algorithm visible; reach forFilteredListonce you are comfortable.
Mix with: React to the Enter key, Keep the table up to date.