0

I am trying to create an HTML editor (learning purposes) and I am trying to make program as dynamic as possible. I want to find FXML variable, like TextField, by it's id and retrieve the text in the textfield when I press the button.

I have 2 Map variables set up

 public FXMLDocumentController() {
        this.HTMLtags = new HashMap<>();
        HTMLtags.put("pressMeButton", "h2.fx-h2");
        HTMLtags.put("setNewsMessageButton", "p.fx-message");

        this.elementsMap = new HashMap<>();
        elementsMap.put("pressMeButton", "newsTitle");
        elementsMap.put("setNewsMessageButton", "newsMessage");
    }

HTMLtags holds the button ID and HTML tag, which is going to be edited. elementsMap holds button ID and TextView ID that it suppose to grab text from. Kinda like "binding specific button to specific textfield".

private void changeText(ActionEvent event) throws IOException {
Object source = event.getSource();
                Button clickedButton = (Button) source;
                System.out.println(HTMLtags.get(clickedButton.getId()));
                System.out.println(elementsMap.get(clickedButton.getId()));
                writeToHTML(HTMLtags.get(clickedButton.getId()), elementsMap.get(clickedButton.getId()));
}

Method above correctly retrieves IDs of buttons and HTML tags that need to be edited.

Here is the code that sets the text in the HTML

private void writeToHTML(String HTMLTag, String textField) {
        File input = new File(filePath);
        Document doc;
        try {
            doc = Jsoup.parse(input, "UTF-8");

            Element head = doc.select(HTMLTag).first();
            originalText = head.toString();

            TextField textFieldName = new TextField();
            textFieldName.setId(textField);
            head.text(textFieldName.getText());


            System.out.println("Original Text is: " + originalText);
            System.out.println("Modified Text is: " + head);

            Path path = Paths.get(filePath);
            Charset charset = StandardCharsets.UTF_8;

            String content = new String(Files.readAllBytes(path), charset);
            content = content.replaceAll(originalText, head.toString());
            Files.write(path, content.getBytes(charset));

        } catch (IOException ex) {
            Logger.getLogger(FXMLDocumentController.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

The problem is that I dunno how to find and retrieve text from textfield specifically in this part of code

TextField textFieldName = new TextField();
                textFieldName.setId(textField);
                head.text(textFieldName.getText());

I want to do it dynamically without declaring each FXML element through @FXML private TextField "name of the fx:id" and matching each element through loop.

I want to do it similar to the way I get ID/value of the button through ActionEvent.

EDIT: here is the full FXML btw

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.*?>
<?import javafx.scene.text.*?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<AnchorPane id="AnchorPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="600.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/8.0.66" xmlns:fx="http://javafx.com/fxml/1" fx:controller="newsletter.editor.FXMLDocumentController">
   <children>
      <ScrollPane fitToWidth="true" prefHeight="800.0" prefWidth="1280.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
         <content>
            <VBox alignment="CENTER" prefHeight="450.0">
               <children>
                  <HBox alignment="CENTER">
                     <children>
                        <Label alignment="CENTER" minHeight="25.0" minWidth="192.0" prefHeight="50.0" prefWidth="192.0" text="Newsletter Title" wrapText="true">
                           <font>
                              <Font size="18.0" />
                           </font>
                        </Label>
                        <TextField id="newsTitle" fx:id="newsTitle" prefHeight="28.0" prefWidth="180.0" promptText="Write here" HBox.hgrow="ALWAYS" />
                     </children>
                  </HBox>
                  <HBox prefHeight="100.0" prefWidth="200.0">
                     <children>
                        <TextArea fx:id="newsMessage" prefHeight="100.0" prefWidth="766.0" />
                     </children>
                  </HBox>
                  <HBox prefHeight="100.0" prefWidth="200.0" />
                  <HBox prefHeight="100.0" prefWidth="200.0" />
                  <HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0">
                     <children>
                        <Button fx:id="setNewsMessageButton" mnemonicParsing="false" onAction="#changeText" text="MAGIC" />
                        <Button fx:id="pressMeButton" mnemonicParsing="false" onAction="#changeText" text="Press Me for Magic">
                           <HBox.margin>
                              <Insets right="10.0" />
                           </HBox.margin>
                        </Button>
                        <Button fx:id="filePathButton" mnemonicParsing="false" onAction="#filePathSave" text="Select Path">
                           <HBox.margin>
                              <Insets right="10.0" />
                           </HBox.margin>
                        </Button>
                        <Button fx:id="popoverButton" mnemonicParsing="false" onAction="#popOverTrigger" text="PopOver Test">
                           <HBox.margin>
                              <Insets right="10.0" />
                           </HBox.margin>
                        </Button>
                     </children>
                  </HBox>
               </children>
               <padding>
                  <Insets left="20.0" right="20.0" />
               </padding>
            </VBox>
         </content>
      </ScrollPane>
   </children>
</AnchorPane>
Nikki Kononov
  • 549
  • 1
  • 8
  • 20

2 Answers2

4

You can perform an id lookup to find the TextField.

Parent parent = getTextFieldParent(); // the Parent (or Scene) that contains the TextFields
TextField textField = (TextField) parent.lookup("#myTextField");
if (textField != null)
    String text = textField.getText();
Prometheus
  • 1,005
  • 6
  • 16
  • Id lookup returns null when searching through the scene. Even if i set id="newsTitle" in the FXML file. Also, when trying to access scene through the controller, I get nullpointerexception. – Nikki Kononov Nov 29 '15 at 08:42
  • I fixed the nullpointer exception but lookup function still returns null id. – Nikki Kononov Nov 29 '15 at 09:08
  • Either the TextField was not added to the scene or the lookup cannot find the id. When you do the lookup, is the id prefixed with a #? That is required. – Prometheus Nov 29 '15 at 13:02
  • yep. I do it like this TextField textField = (TextField) parent.lookup("#" + myTextField"); where myTextField is a variable passed to method. – Nikki Kononov Nov 29 '15 at 15:49
  • When executing that line I get ClassCastException: Collections&EmptySet cannot be cast to TextField – Nikki Kononov Nov 29 '15 at 16:41
  • Problem was that I made an assumption that lookup method looks up through the entire tree. As I was using Anchor Pane as parent, where I was suppose to go for HBox. However, is it possible to loop through child elements of child elements as I have them all wrapped around VBox? – Nikki Kononov Nov 29 '15 at 19:13
0

Yes, your need method lookup(). Not only Parent class has this method, it also has at Panel classes like FlowPane, AnchorPane and many other. Also this method in Scene class.

But! This method looking only after scene show. I don't now, why it happens, but before Scene show(), lookup() only finds top-level panel. And after show() it finds element by id, like scene.lookup("#myId"); Symbol # is required.

Also lookup() method can find element without id, by the type.

Example: scene.lookup("FlowPane"); (without #) will find the first FlowPane in your .fxml file. And scene.lookupAll("ImageView"); will return the massive with all ImageView elements in scene.

DaFois
  • 2,197
  • 8
  • 26
  • 43
Roman Bush
  • 156
  • 1
  • 10
  • Roman, what `show()` is doing is applying css and running a layout pass to completely initialize the scenegraph. You can get the same effect, without show, by calling `applyCss()` and `layout()` on the node after the node has been attached to a scene (the scene doesn't need to be displayed or shown for this). After that, `lookup()` by CSS selectors will work as expected (see [Get the height of a node in JavaFX (generate a layout pass)](https://stackoverflow.com/questions/26152642/get-the-height-of-a-node-in-javafx-generate-a-layout-pass) for info. – jewelsea Oct 04 '19 at 22:49
  • jewelsea, thanks for the comment, now it became clear to me. – Roman Bush Oct 06 '19 at 10:29