Add a person-desk example and a guide for the fx library.

The example replaces the call sites of the removed JavaFX units so the module can build again.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-30 19:38:48 +01:00
co-authored by Cursor
parent 398883d20a
commit ab158db3e7
27 changed files with 1249 additions and 546 deletions
@@ -1,29 +0,0 @@
package de.neitzel.fx.component.example;
import lombok.Getter;
import lombok.Setter;
/**
* Simple address model used in the FX example component.
*/
@Getter
@Setter
public class Address {
/**
* Street name and number.
*/
private String street;
/**
* City name.
*/
private String city;
/**
* Default constructor only
*/
public Address() {
// default constructor only
}
}
@@ -1,56 +0,0 @@
package de.neitzel.fx.component.example;
import de.neitzel.fx.component.ComponentLoader;
import javafx.application.Application;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
/**
* Example JavaFX application demonstrating the {@link de.neitzel.fx.component.ComponentLoader} usage.
*/
public class ExampleApp extends Application {
/**
* Default constructor only
*/
public ExampleApp() {
// default constructor only
}
/**
* Launches the JavaFX application.
*
* @param args command line arguments
*/
public static void main(String[] args) {
launch(args);
}
/**
* Initializes sample model data, loads the FX component and shows the primary stage.
*
* @param primaryStage the primary stage for this application
*/
@Override
public void start(Stage primaryStage) throws Exception {
// Beispielmodel initialisieren
Address address = new Address();
address.setStreet("Sample Street 1");
address.setCity("Sample City");
Person person = new Person();
person.setName("Max Mustermann");
person.setAddress(address);
// ComponentLoader verwenden
ComponentLoader loader = new ComponentLoader();
Parent root = loader.load(person, "/person.fxml");
// Scene erstellen und anzeigen
Scene scene = new Scene(root);
primaryStage.setTitle("ComponentLoader Example");
primaryStage.setScene(scene);
primaryStage.show();
}
}
@@ -1,24 +0,0 @@
package de.neitzel.fx.component.example;
/**
* Another Main class as workaround when the JavaFX Application ist started without
* taking care os Classloader Requirements of JavaFX. (Important when starting from inside NetBeans!)
*/
public class Main {
/**
* Default constructor only
*/
public Main() {
// default constructor only
}
/**
* Additional main methode to start Application.
*
* @param args Commandline Arguments.
*/
public static void main(String[] args) {
ExampleApp.main(args);
}
}
@@ -1,29 +0,0 @@
package de.neitzel.fx.component.example;
import lombok.Getter;
import lombok.Setter;
/**
* Simple person model used in the FX example component.
*/
@Getter
@Setter
public class Person {
/**
* The person's full name.
*/
private String name;
/**
* The address of the person.
*/
private Address address;
/**
* Default constructor only
*/
public Person() {
// default constructor only
}
}
@@ -0,0 +1,70 @@
package de.neitzel.fx.example;
import de.neitzel.fx.example.model.Address;
import de.neitzel.fx.example.model.Person;
import de.neitzel.fx.example.view.DeskController;
import de.neitzel.fx.view.LoadedView;
import de.neitzel.fx.view.MessagePopup;
import de.neitzel.fx.view.ViewLoader;
import de.neitzel.injection.ApplicationContext;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.util.ArrayList;
import java.util.List;
/**
* Opens the person desk for one ordinary {@link Person}.
*/
public class DeskApp extends Application {
/**
* Creates the JavaFX application.
*/
public DeskApp() {
}
/**
* Builds the person the desk edits.
*
* @return a person with a home address, one visit, and two tags
*/
public static Person sample() {
Address home = new Address("Sample Street 1", "Sample City");
Address visit = new Address("Work Road 2", "Office City");
Person person = new Person();
person.setName("Ada Lovelace");
person.setAge(36);
person.setHome(home);
person.setVisits(new ArrayList<>(List.of(visit)));
person.setSelectedVisit(visit);
person.setTags(new ArrayList<>(List.of("dns", "math")));
return person;
}
/**
* Launches the desk.
*
* @param args unused
*/
public static void main(String[] args) {
launch(args);
}
/**
* Loads the desk for {@link #sample()} and shows it.
*
* @param stage the primary stage
*/
@Override
public void start(Stage stage) {
ApplicationContext context = new ApplicationContext(DeskApp.class);
Person person = sample();
LoadedView loaded = ViewLoader.load(DeskController.class.getResource("Desk.fxml"), person, context);
MessagePopup.showNewMessages(loaded.getViewModel());
stage.setTitle("fx example");
stage.setScene(new Scene(loaded.getRoot()));
stage.show();
}
}
@@ -0,0 +1,23 @@
package de.neitzel.fx.example;
import javafx.application.Application;
/**
* Launches the example without extending {@link Application}.
* <p>
* JavaFX on the classpath is started from a class that is not itself an {@link Application}.
*/
public final class Main {
private Main() {
}
/**
* Starts the person desk.
*
* @param args unused
*/
public static void main(String[] args) {
Application.launch(DeskApp.class, args);
}
}
@@ -0,0 +1,73 @@
package de.neitzel.fx.example.model;
/**
* An ordinary address the desk edits.
*/
public class Address {
private String street = "";
private String city = "";
/**
* Creates an empty address.
*/
public Address() {
}
/**
* Creates an address with the given street and city.
*
* @param street the street
* @param city the city
*/
public Address(String street, String city) {
this.street = street;
this.city = city;
}
/**
* Returns the street.
*
* @return the street
*/
public String getStreet() {
return street;
}
/**
* Sets the street.
*
* @param street the street
*/
public void setStreet(String street) {
this.street = street;
}
/**
* Returns the city.
*
* @return the city
*/
public String getCity() {
return city;
}
/**
* Sets the city.
*
* @param city the city
*/
public void setCity(String city) {
this.city = city;
}
/**
* Returns the street and city, for a combo box row.
*
* @return the street and city
*/
@Override
public String toString() {
return street + ", " + city;
}
}
@@ -0,0 +1,137 @@
package de.neitzel.fx.example.model;
import java.util.ArrayList;
import java.util.List;
/**
* An ordinary person the desk edits.
* <p>
* Read-write attributes are what the View binds to. The class carries no JavaFX types.
*/
public class Person {
private String name = "";
private Integer age;
private Address home;
private List<Address> visits = new ArrayList<>();
private Address selectedVisit;
private List<String> tags = new ArrayList<>();
/**
* Creates an empty person.
*/
public Person() {
}
/**
* Returns the name.
*
* @return the name
*/
public String getName() {
return name;
}
/**
* Sets the name.
*
* @param name the name
*/
public void setName(String name) {
this.name = name;
}
/**
* Returns the age in years.
*
* @return the age, or {@code null} when none is set
*/
public Integer getAge() {
return age;
}
/**
* Sets the age in years.
*
* @param age the age
*/
public void setAge(Integer age) {
this.age = age;
}
/**
* Returns the home address.
*
* @return the home address, or {@code null} when none is set
*/
public Address getHome() {
return home;
}
/**
* Sets the home address.
*
* @param home the home address
*/
public void setHome(Address home) {
this.home = home;
}
/**
* Returns the visits.
* <p>
* After the View has loaded, this is the list the View is watching.
*
* @return the visits
*/
public List<Address> getVisits() {
return visits;
}
/**
* Sets the visits.
*
* @param visits the visits
*/
public void setVisits(List<Address> visits) {
this.visits = visits;
}
/**
* Returns the visit chosen in the combo box.
*
* @return the selected visit, or {@code null} when none is selected
*/
public Address getSelectedVisit() {
return selectedVisit;
}
/**
* Sets the visit chosen in the combo box.
*
* @param selectedVisit the selected visit
*/
public void setSelectedVisit(Address selectedVisit) {
this.selectedVisit = selectedVisit;
}
/**
* Returns the tags.
* <p>
* After the View has loaded, this is the list the View is watching.
*
* @return the tags
*/
public List<String> getTags() {
return tags;
}
/**
* Sets the tags.
*
* @param tags the tags
*/
public void setTags(List<String> tags) {
this.tags = tags;
}
}
@@ -0,0 +1,51 @@
package de.neitzel.fx.example.view;
import jakarta.inject.Singleton;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.scene.layout.VBox;
/**
* Component that edits one address.
* <p>
* The class is the root and the Controller of {@code AddressForm.fxml}. A parent View sets
* {@link #modelProperty()} from a Direct binding. A repeated row gets its own instance.
*/
@Singleton
public class AddressForm extends VBox {
private final ObjectProperty<Object> model = new SimpleObjectProperty<>(this, "model");
/**
* Creates an empty form. The container calls this constructor.
*/
public AddressForm() {
}
/**
* Returns the model property a parent View sets.
*
* @return the model property
*/
public ObjectProperty<Object> modelProperty() {
return model;
}
/**
* Returns the address this form edits.
*
* @return the address, or {@code null} when none is set
*/
public Object getModel() {
return model.get();
}
/**
* Sets the address this form edits.
*
* @param model the address
*/
public void setModel(Object model) {
this.model.set(model);
}
}
@@ -0,0 +1,58 @@
package de.neitzel.fx.example.view;
import de.neitzel.fx.view.Converter;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
/**
* Translates an age between the Model and the text field.
*/
@Singleton
public class AgeConverter implements Converter {
private final AgeSuffix suffix;
/**
* Creates a converter that reads its suffix from the injected service.
*
* @param suffix the suffix written in front of the age
*/
@Inject
public AgeConverter(AgeSuffix suffix) {
this.suffix = suffix;
}
/**
* Writes the suffix in front of the age.
*
* @param attributeValue the age
* @return the text shown in the field
*/
@Override
public Object toControlValue(Object attributeValue) {
if (attributeValue == null) {
return "";
}
return suffix.prefix() + attributeValue;
}
/**
* Reads an age that starts with the suffix.
*
* @param controlValue the field text
* @return the age
*/
@Override
public Object toAttributeValue(Object controlValue) {
String text = controlValue == null ? "" : controlValue.toString();
String prefix = suffix.prefix();
if (!text.startsWith(prefix)) {
throw new IllegalArgumentException("Age must start with \"" + prefix.trim() + "\": " + text);
}
try {
return Integer.valueOf(text.substring(prefix.length()).trim());
} catch (NumberFormatException failure) {
throw new IllegalArgumentException("Age must be a whole number: " + text);
}
}
}
@@ -0,0 +1,25 @@
package de.neitzel.fx.example.view;
import jakarta.inject.Singleton;
/**
* The suffix an {@link AgeConverter} writes in front of an age.
*/
@Singleton
public class AgeSuffix {
/**
* Creates the suffix used by the desk.
*/
public AgeSuffix() {
}
/**
* Returns the text written in front of the age.
*
* @return the suffix, including its trailing space
*/
public String prefix() {
return "years ";
}
}
@@ -0,0 +1,104 @@
package de.neitzel.fx.example.view;
import de.neitzel.fx.example.model.Address;
import de.neitzel.fx.view.HasViewModel;
import de.neitzel.fx.view.Message;
import de.neitzel.fx.view.ViewModel;
import jakarta.inject.Singleton;
import javafx.fxml.FXML;
import javafx.scene.control.TextField;
import java.util.List;
/**
* Controller for the person desk.
* <p>
* The container creates this class and injects nothing else. {@link #setViewModel} receives the
* ViewModel before the binding elements run, so {@link #greeting()} can read the name field on
* its first call.
*/
@Singleton
public class DeskController implements HasViewModel {
@FXML
private TextField nameField;
@FXML
private TextField tagField;
private ViewModel viewModel;
/**
* Creates the controller. The container calls this constructor.
*/
public DeskController() {
}
/**
* Receives the ViewModel for the person on the desk.
*
* @param viewModel the ViewModel
*/
@Override
public void setViewModel(ViewModel viewModel) {
this.viewModel = viewModel;
}
/**
* Returns the greeting written to the label.
* <p>
* The method reads the name field. It does not write the Model.
*
* @return the greeting
*/
public String greeting() {
String name = nameField.getText();
if (name == null || name.isBlank()) {
return "Hello";
}
return "Hello, " + name.trim();
}
/**
* Adds a visit and leaves the previous selection in place.
*/
@FXML
public void addVisit() {
visits().add(new Address("New street", "New city"));
}
/**
* Adds the tag field's text to the tag list and clears the field.
*/
@FXML
public void addTag() {
String tag = tagField.getText();
if (tag == null || tag.isBlank()) {
return;
}
tags().add(tag.trim());
tagField.clear();
}
/**
* Adds an info Message that quotes the name already stored on the Model.
*/
@FXML
public void leaveNote() {
Object name = viewModel.property("name").getValue();
viewModel.addMessage(new Message(
Message.Severity.INFO,
"The model already has the name " + name + ".",
"desk"));
}
@SuppressWarnings("unchecked")
private List<Address> visits() {
return (List<Address>) viewModel.property("visits").getValue();
}
@SuppressWarnings("unchecked")
private List<String> tags() {
return (List<String>) viewModel.property("tags").getValue();
}
}
@@ -1,49 +0,0 @@
package de.neitzel.fx.injectfx.example;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Minimal JavaFX application launcher used by the examples.
*
* <p>This class initializes the JavaFX runtime, loads {@code MainWindow.fxml} and shows
* the primary stage. It is intended as a small bootstrap used by IDEs or test runs
* to start the JavaFX UI for demonstration purposes.
*/
public class JavaFXApp extends Application {
private static final Logger LOGGER = Logger.getLogger(JavaFXApp.class.getName());
/**
* Default constructor only.
*
* <p>The constructor does not perform UI initialization. UI setup happens in {@link #start(Stage)}
* after JavaFX has set up the application thread.
*/
public JavaFXApp() {
// default constructor only
}
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
try {
primaryStage.setTitle("Hello World!");
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("MainWindow.fxml"));
Parent root = fxmlLoader.load();
primaryStage.setScene(new Scene(root));
primaryStage.show();
} catch (Exception ex) {
LOGGER.log(Level.SEVERE, "Exception while starting JavaFXApp", ex);
}
}
}
@@ -1,33 +0,0 @@
package de.neitzel.fx.injectfx.example;
/**
* Another Main class as workaround when the JavaFX Application ist started without
* taking care os Classloader Requirements of JavaFX. (Important when starting from inside NetBeans!)
*/
public class Main {
/**
* Creates a new helper instance of {@code Main}.
*
* <p>This constructor is intentionally empty. The {@code Main} class serves as an alternative
* entry point to launch the JavaFX application via {@link JavaFXApp#main(String[])} in
* environments (IDEs, build tools) where directly starting a class that extends
* {@code javafx.application.Application} may fail due to classloader or JVM startup constraints.
*
* <p>Do not perform application initialization here; use the static {@code main} method to
* start the JavaFX runtime. This constructor exists to allow tooling and frameworks to
* instantiate the class reflectively if required.
*/
public Main() {
// default constructor only
}
/**
* Additional main methode to start Application.
*
* @param args Commandline Arguments.
*/
public static void main(String[] args) {
JavaFXApp.main(args);
}
}
@@ -1,68 +0,0 @@
package de.neitzel.fx.injectfx.example;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TextField;
import java.net.URL;
import java.util.ResourceBundle;
/**
* Controller for the MainWindow FXML view.
*
* <p>This class acts as the backing controller for the {@code MainWindow.fxml} UI. It demonstrates a
* minimal example of a JavaFX controller that:
* <ul>
* <li>receives UI element injection via {@link FXML} (the {@link #textField} field),</li>
* <li>initializes the view in {@link #initialize(URL, ResourceBundle)}, and</li>
* <li>handles user actions via {@link #onButtonClick(ActionEvent)}.</li>
* </ul>
*
* <p>Usage notes:
* <ul>
* <li>Fields annotated with {@link FXML} are injected by the {@code FXMLLoader} before
* {@link #initialize(URL, ResourceBundle)} is called — do not expect them to be non-null
* in the constructor.</li>
* <li>All UI updates must run on the JavaFX Application Thread. Methods called by FXML
* (such as {@link #onButtonClick(ActionEvent)}) are executed on that thread by the
* JavaFX runtime.</li>
* <li>The controller keeps a simple click counter and updates {@link #textField} to reflect
* the current count using {@link #displayCounter()}.</li>
* </ul>
*/
public class MainWindow implements Initializable {
/**
* Counter for button clicks shown in the UI.
*/
private int counter = 0;
/**
* Text field UI control showing the click counter.
*/
@FXML
private TextField textField;
/**
* Default constructor only
*/
public MainWindow() {
// default constructor only
}
@Override
public void initialize(URL location, ResourceBundle resources) {
displayCounter();
}
private void displayCounter() {
textField.setText("Click counter: " + counter);
}
@FXML
private void onButtonClick(ActionEvent actionEvent) {
counter++;
displayCounter();
}
}
@@ -1,16 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane xmlns:fx="http://javafx.com/fxml"
xmlns:nfx="http://example.com/nfx"
xmlns="http://javafx.com/javafx"
fx:controller="de.neitzel.fx.component.ComponentController"
prefWidth="300" prefHeight="100">
<children>
<TextField layoutX="10" layoutY="10" prefWidth="280"
nfx:target="street" nfx:direction="bidirectional"/>
<TextField layoutX="10" layoutY="50" prefWidth="280"
nfx:target="city" nfx:direction="bidirectional"/>
</children>
</AnchorPane>
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.VBox?>
<?import de.neitzel.fx.view.BindingList?>
<?import de.neitzel.fx.view.DirectBinding?>
<fx:root type="javafx.scene.layout.VBox" xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml" spacing="6">
<padding>
<Insets top="4" right="4" bottom="4" left="4"/>
</padding>
<fx:define>
<BindingList>
<DirectBinding control="streetField" property="text" path="street" direction="both"/>
<DirectBinding control="cityField" property="text" path="city" direction="both"/>
</BindingList>
</fx:define>
<Label text="Street"/>
<TextField fx:id="streetField" id="streetField"/>
<Label text="City"/>
<TextField fx:id="cityField" id="cityField"/>
</fx:root>
@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ComboBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<?import de.neitzel.fx.example.view.AddressForm?>
<?import de.neitzel.fx.view.BindingList?>
<?import de.neitzel.fx.view.ComputedBinding?>
<?import de.neitzel.fx.view.DirectBinding?>
<?import de.neitzel.fx.view.Repeat?>
<VBox xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml"
fx:controller="de.neitzel.fx.example.view.DeskController"
spacing="8" prefWidth="520">
<padding>
<Insets top="12" right="12" bottom="12" left="12"/>
</padding>
<fx:define>
<BindingList>
<DirectBinding control="nameField" property="text" path="name" direction="both"/>
<DirectBinding control="ageField" property="text" path="age" direction="both"
converter="de.neitzel.fx.example.view.AgeConverter"/>
<ComputedBinding control="greetingLabel" property="text" method="#greeting"/>
<DirectBinding control="homeForm" property="model" path="home" direction="both"/>
<DirectBinding control="homeStreetLabel" property="text" path="home.street" direction="view"/>
<Repeat pane="visitPane" component="de.neitzel.fx.example.view.AddressForm" collection="visits"/>
<DirectBinding control="visitChoice" property="items" path="visits" direction="view"/>
<DirectBinding control="visitChoice" property="value" path="selectedVisit" direction="both"/>
<DirectBinding control="tagList" property="items" path="tags" direction="view"/>
<DirectBinding control="noticeLabel" property="text" path="latestMessage" direction="view"/>
</BindingList>
</fx:define>
<Label text="Person" style="-fx-font-size: 16px;"/>
<GridPane hgap="8" vgap="6">
<columnConstraints>
<ColumnConstraints minWidth="110"/>
<ColumnConstraints hgrow="ALWAYS" minWidth="280"/>
</columnConstraints>
<Label text="Name" GridPane.rowIndex="0" GridPane.columnIndex="0"/>
<TextField fx:id="nameField" id="nameField" GridPane.rowIndex="0" GridPane.columnIndex="1"/>
<Label text="Age" GridPane.rowIndex="1" GridPane.columnIndex="0"/>
<TextField fx:id="ageField" id="ageField" promptText="years 36"
GridPane.rowIndex="1" GridPane.columnIndex="1"/>
<Label text="Greeting" GridPane.rowIndex="2" GridPane.columnIndex="0"/>
<Label fx:id="greetingLabel" id="greetingLabel" GridPane.rowIndex="2" GridPane.columnIndex="1"/>
<Label text="Home street" GridPane.rowIndex="3" GridPane.columnIndex="0"/>
<Label fx:id="homeStreetLabel" id="homeStreetLabel" GridPane.rowIndex="3" GridPane.columnIndex="1"/>
</GridPane>
<Label text="Home"/>
<AddressForm fx:id="homeForm"/>
<Label text="Visits"/>
<HBox spacing="8">
<ComboBox fx:id="visitChoice" id="visitChoice" HBox.hgrow="ALWAYS" maxWidth="Infinity"/>
<Button text="Add visit" onAction="#addVisit"/>
</HBox>
<VBox fx:id="visitPane" id="visitPane" spacing="4"/>
<Label text="Tags"/>
<ListView fx:id="tagList" id="tagList" prefHeight="80"/>
<HBox spacing="8">
<TextField fx:id="tagField" id="tagField" promptText="Tag" HBox.hgrow="ALWAYS"/>
<Button text="Add tag" onAction="#addTag"/>
</HBox>
<Label text="Notice"/>
<Label fx:id="noticeLabel" id="noticeLabel" wrapText="true"/>
<Button text="Leave a note" onAction="#leaveNote"/>
</VBox>
@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane xmlns:fx="http://javafx.com/fxml/1" prefHeight="127.0" prefWidth="209.0"
xmlns="http://javafx.com/javafx/17.0.2-ea" fx:controller="de.neitzel.fx.injectfx.example.MainWindow">
<children>
<Button fx:id="button" layoutX="44.0" layoutY="70.0" mnemonicParsing="false" onAction="#onButtonClick"
text="Click Me"/>
<TextField fx:id="textField" layoutX="14.0" layoutY="24.0"/>
</children>
</AnchorPane>
-18
View File
@@ -1,18 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.Pane?>
<AnchorPane xmlns:fx="http://javafx.com/fxml"
xmlns:nfx="http://example.com/nfx"
xmlns="http://javafx.com/javafx"
fx:controller="de.neitzel.fx.component.ComponentController"
prefWidth="300" prefHeight="180">
<children>
<TextField layoutX="10" layoutY="10" prefWidth="280"
nfx:target="name" nfx:direction="bidirectional"/>
<Pane layoutX="10" layoutY="50" prefWidth="280" prefHeight="110"
nfx:target="address" nfx:direction="bidirectional"
nfx:source="/address.fxml"/>
</children>
</AnchorPane>