Example Task
Create a JavaFX application called “Registration Form” that contains:
- a username field (TextField);
- a password field (PasswordField);
- a “Register” button (Button);
- a status text field (Label).
The application should:
- validate the data in real time;
- enable the button only when the data is valid.
| 1. Implementation Using the Imperative Model | 2. Implementation Using the Reactive Model |
| 1.1 Application Structure | |
ps-project/
└─ src/
└─ main/
├─ java/
│ └─ bg/
│ └─ tu_varna/
│ └─ sit/
│ └─ ps/
│ └─ lab4/
│ └─ task1/
│ ├─ RegistrationApplication.java
│ ├─ Launcher.java
│ └─ controller/
│ └─ RegistrationController.java
└─ resources/
└─ bg/
└─ tu_varna/
└─ sit/
└─ ps/
└─ lab4/
└─ task1/
└─ registration-view.fxml
|
ps-project/
└─ src/
└─ main/
├─ java/
│ └─ bg/
│ └─ tu_varna/
│ └─ sit/
│ └─ ps/
│ └─ lab4/
│ └─ task2/
│ ├─ RegistrationApplication.java
│ ├─ Launcher.java
│ └─ controller/
│ └─ RegistrationController.java
└─ resources/
└─ bg/
└─ tu_varna/
└─ sit/
└─ ps/
└─ lab4/
└─ task2/
└─ registration-view.fxml
|
| 3. Controller |
In the imperative approach: - the logic is executed only when an event occurs;
- the validation is performed manually;
- there is no automatic reaction when the data changes.
| In the reactive approach: - JavaFX Properties are used;
- reacts in real time;
- automatically manages the UI through binding.
|
package controller;
import javafx.fxml.FXML;
import javafx.scene.control.*;
public class RegistrationController {
@FXML
private TextField usernameField;
@FXML
private PasswordField passwordField;
@FXML
private Button registerButton;
@FXML
private Label statusLabel;
@FXML
public void initialize() {
registerButton.setOnAction(e -> {
String username = usernameField.getText();
String password = passwordField.getText();
if (username.length() >= 4 && password.length() >= 6) {
statusLabel.setText("The data is valid.");
} else {
statusLabel.setText("Invalid data");
}
});
}
}
| package controller;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.event.ActionEvent;
public class RegistrationController {
@FXML
private TextField usernameField;
@FXML
private PasswordField passwordField;
@FXML
private Button registerButton;
@FXML
private Label statusLabel;
// JavaFX Properties (reactive state)
private final BooleanProperty validUsername =
new SimpleBooleanProperty(false);
private final BooleanProperty validPassword =
new SimpleBooleanProperty(false);
@FXML
public void initialize() {
/* ===== Observer (ChangeListener + addListener) ===== */
usernameField.textProperty().addListener(
(obs, oldVal, newVal) ->
validUsername.set(newVal.length() >= 4)
);
passwordField.textProperty().addListener(
(obs, oldVal, newVal) ->
validPassword.set(newVal.length() >= 6)
);
/* ===== Binding ===== */
BooleanBinding formInvalid =
validUsername.not().or(validPassword.not());
registerButton.disableProperty().bind(formInvalid);
/* ===== Observer върху Binding ===== */
formInvalid.addListener((obs, oldVal, newVal) -> {
if (newVal) {
statusLabel.setText("Invalid data");
} else {
statusLabel.setText("The data is valid");
}
});
/* ===== EventHandler ===== */
registerButton.setOnAction(this::handleRegister);
/* ===== addEventHandler ===== */
registerButton.addEventHandler(
ActionEvent.ACTION,
e -> System.out.println("Processed ActionEvent")
);
}
private void handleRegister(ActionEvent event) {
statusLabel.setText("Registration successful!");
}
}
|
- Validation occurs only when the button is pressed
- The UI does not react while typing
- Automatic synchronization is missing
- This approach is typical of the imperative model
| - Validation occurs in real time
- The UI reacts to every change
- There is automatic synchronization through binding
- This approach is typical of the reactive model
|
| 4. FXML file – user interface – registration-view.fxml |
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.VBox?>
<VBox spacing="10" alignment="CENTER"
xmlns:fx="http://javafx.com/fxml"
fx:controller="bg.tu_varna.sit.ps.lab4.task1.controller.RegistrationController">
<TextField fx:id="usernameField"
promptText="Username"/>
<PasswordField fx:id="passwordField"
promptText="Password"/>
<Button fx:id="registerButton"
text="Registration"/>
<Label fx:id="statusLabel"
text="Enter data"/>
</VBox>
| <?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.VBox?>
<VBox spacing="10" alignment="CENTER"
xmlns:fx="http://javafx.com/fxml"
fx:controller="bg.tu_varna.sit.ps.lab4.task2.controller.RegistrationController">
<TextField fx:id="usernameField"
promptText="Username"/>
<PasswordField fx:id="passwordField"
promptText="Password"/>
<Button fx:id="registerButton"
text="Registration"/>
<Label fx:id="statusLabel"
text="Enter data"/>
</VBox>
|
- FXML describes only the UI, without logic.
- fx:id provides a connection to the controller
|
| 5. Application class |
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class RegistrationApplication extends Application {
@Override
public void start(Stage stage) throws Exception {
FXMLLoader loader = new FXMLLoader(
RegistrationApplication.class
.getResource("registration-view.fxml")
);
Parent root = loader.load();
Scene scene = new Scene(root, 300, 200);
stage.setTitle("JavaFX FXML imperative Example");
stage.setScene(scene);
stage.show();
}
}
| import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class RegistrationApplication extends Application {
@Override
public void start(Stage stage) throws Exception {
FXMLLoader loader = new FXMLLoader(
RegistrationApplication.class
.getResource("registration-view.fxml")
);
Parent root = loader.load();
Scene scene = new Scene(root, 300, 200);
stage.setTitle("JavaFX FXML Reactive Example");
stage.setScene(scene);
stage.show();
}
}
|
| 6. Launcher class |
import javafx.application.Application;
public class Launcher {
public static void main(String[] args) {
Application.launch(RegistrationApplication.class, args);
}
}
|
6. Comparison of FXML: Imperative vs Reactive
| Criterion | Imperative | Reactive |
| Reaction during input | No | Yes |
| Properties | No | Yes |
| Binding | No | Yes |
| Listeners | Limited | Active |
| Maintenance | More difficult | Easier |