0

I have a custom component representing a form field. It has a label, a text field and an error message that may be shown after input validation.

<fx:root maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="109.0" prefWidth="512.0" spacing="10.0" styleClass="vbox" type="VBox" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1"> <children> <Label fx:id="fieldLabel" text="Lorem ipsum dolor sit amet"></Label> <TextField fx:id="textField" promptText="Lorem ipsum dolor sit amet"></TextField> <Label fx:id="errorLabel" text=""></Label> </children> </fx:root> 
public class FormField extends VBox { @FXML private TextField textField; @FXML private Label fieldLabel; @FXML private Label errorLabel; private String fieldLabelText; private String promptText; public FormField(@NamedArg("fieldLabelText") String fieldLabelText, @NamedArg("promptText") String promptText, @NamedArg("isPasswordField") boolean isPasswordField) { FXMLLoader loader = new FXMLLoader(getClass().getResource("../../resources/fxml/form-field.fxml")); loader.setRoot(this); loader.setController(this); this.fieldLabelText = fieldLabelText; this.promptText = promptText; try { loader.load(); } catch (IOException exception) { throw new RuntimeException(exception); } } @FXML public void initialize() { this.fieldLabel.setText(fieldLabelText); this.textField.setPromptText(promptText); } 

Now what I want to know is how would I go about making an extension of this component that has a PasswordField instead of a TextField? Or passing an argument such as boolean isPasswordField that let's FormField decide if it should render a TextField or a PasswordField? If TextField had an obscureText(true) method in it's API it would be good enough since this is all I'm looking for, but I couldn't find any.

All I could find about JavaFX inheritance was in the sense of "extending" the object by adding new components to it, not by changing it's existing elements.

1
  • 4
    As an aside, note that the path to your FXML is incorrect (it will not work when your application is packaged). See stackoverflow.com/questions/61531317/…. Commented Jun 24, 2020 at 11:17

1 Answer 1

4

One option is to place both a TextField and a PasswordField in the UI, stacked on top of each other in a StackPane, with only one of them visible:

<fx:root maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="109.0" prefWidth="512.0" spacing="10.0" styleClass="vbox" type="VBox" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1"> <children> <Label fx:id="fieldLabel" text="Lorem ipsum dolor sit amet"></Label> <StackPane> <TextField fx:id="textField" promptText="Lorem ipsum dolor sit amet"></TextField> <PasswordField fx:id="passwordField" visible="false"/> </StackPane> <Label fx:id="errorLabel" text=""></Label> </children> </fx:root> 

Then in the controller decide which is visible based on the parameter that was passed:

public class FormField extends VBox { @FXML private TextField textField; @FXML private PasswordField passwordField; @FXML private Label fieldLabel; @FXML private Label errorLabel; private String fieldLabelText; private String promptText; private boolean isPasswordField; public FormField(@NamedArg("fieldLabelText") String fieldLabelText, @NamedArg("promptText") String promptText, @NamedArg("isPasswordField") boolean isPasswordField) { // path is copied from OP, but is incorrect: FXMLLoader loader = new FXMLLoader(getClass().getResource("../../resources/fxml/form-field.fxml")); loader.setRoot(this); loader.setController(this); this.fieldLabelText = fieldLabelText; this.promptText = promptText; this.isPasswordField = passwordField; try { loader.load(); } catch (IOException exception) { throw new RuntimeException(exception); } } @FXML public void initialize() { this.fieldLabel.setText(fieldLabelText); this.textField.setPromptText(promptText); this.passwordField.promptTextProperty().bind( this.textField.promptTextProperty()); this.passwordField.setVisible(isPasswordField); this.textField.setVisible(!isPasswordField); } } 

Other variations are possible, e.g. your FXML could just define the StackPane with no content; then in the controller code add either a TextField or PasswordField as needed.

Sign up to request clarification or add additional context in comments.

2 Comments

propagating that incorrect parameter in resource lookup .. doohh *tongue-in-cheek :)
As an addition to this, If he needs to hide or show the text later, He can use a boolean property instead of a boolean, for whether or not the text is hidden, and bind visible properties of the fields to it, and maybe bind the text properties of the fields to each other (bidirectional)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.