javafx – FXML and Controllers – Setting Properties

There are multiple ways of adding data to a object in fxml:

<property> tag

A tag with the name of a property can be added as child of an element used for creating a instance. The child of this tag is assigned to the property using the setter or added to the contents of the property (readonly list/map properties).

Default property

A class can be annotated with the @DefaultProperty annotation. In this case elements can be directly added as child element without using a element with the name of the property.

property="value" attribute

Properties can be assigned using the property name as attribute name and the value as attribute value. This has the same effect as adding the following element as child of the tag:

<property>
    <String fx:value="value" />
</property>

static setters

Properties can be set using static setters too. These are static methods named setProperty that take the element as first parameter and the value to set as second parameter. Those methods can recide in any class and can be used using ContainingClass.property instead of the usual property name.

Note: Currently it seems to be neccesary to have a corresponding static getter method (i.e. a static method named getProperty taking the element as parameter in the same class as the static setter) for this to work unless the value type is String.

Type Coercion

The following mechanism is used to get a object of the correct class during assignments, e.g. to suit the parameter type of a setter method.

If the classes are assignable, then the value itself is used.

Otherwise the value is converted as follows

Target type value used (source value s)
Boolean, boolean Boolean.valueOf(s)
char, Character s.toString.charAt(0)
other primitive type or wrapper type appropriate method for target type, in case the s is a Number, the valueOf(s.toString()) for the wrapper type otherwise
BigInteger BigInteger.valueOf(s.longValue()) is s is a Number, new BigInteger(s.toString()) otherwise
BigDecimal BigDecimal.valueOf(s.doubleValue()) is s is a Number, new BigDecimal(s.toString()) otherwise
Number Double.valueOf(s.toString()) if s.toString() contains a ., Long.valueOf(s.toString()) otherwise
Class Class.forName(s.toString()) invoked using the context ClassLoader of the current thread without initializing the class
enum The result of the valueOf method, additionally converted to an all uppercase String seperated by _ inserted before each uppercase letter, if s is a String that starts with a lowercase letter
other the value returned by a static valueOf method in the targetType, that has a parameter matching the type of s or a superclass of that type

Note: This behavior isn’t well-documented and could be subject to change.

Example

public enum Location {
    WASHINGTON_DC,
    LONDON;
}
package fxml.sample;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javafx.beans.DefaultProperty;

@DefaultProperty("items")
public class Sample {
    
    private Location loaction;

    public Location getLoaction() {
        return loaction;
    }

    public void setLoaction(Location loaction) {
        this.loaction = loaction;
    }

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }
    
    int number;

    private final List<Object> items = new ArrayList<>();

    public List<Object> getItems() {
        return items;
    }
    
    private final Map<String, Object> map = new HashMap<>();

    public Map<String, Object> getMap() {
        return map;
    }
    
    private BigInteger serialNumber;

    public BigInteger getSerialNumber() {
        return serialNumber;
    }

    public void setSerialNumber(BigInteger serialNumber) {
        this.serialNumber = serialNumber;
    }

    @Override
    public String toString() {
        return "Sample{" + "loaction=" + loaction + ", number=" + number + ", items=" + items + ", map=" + map + ", serialNumber=" + serialNumber + '}';
    }
    
}
package fxml.sample;

public class Container {

    public static int getNumber(Sample sample) {
        return sample.number;
    }

    public static void setNumber(Sample sample, int number) {
        sample.number = number;
    }

    private final String value;

    private Container(String value) {
        this.value = value;
    }

    public static Container valueOf(String s) {
        return new Container(s);
    }

    @Override
    public String toString() {
        return "42" + value;
    }

}

Printing the result of loading the below fxml file yields

Sample{loaction=WASHINGTON_DC, number=5, items=[42a, 42b, 42c, 42d, 42e, 42f], map={answer=42, g=9.81, hello=42A, sample=Sample{loaction=null, number=33, items=[], map={}, serialNumber=null}}, serialNumber=4299}
<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import fxml.sample.*?>

<Sample xmlns:fx="http://javafx.com/fxml/1" Container.number="5" loaction="washingtonDc">
    
    <!-- set serialNumber property (type coercion) -->
    <serialNumber>
        <Container fx:value="99"/>
    </serialNumber>
    
    <!-- Add elements to default property-->
    <Container fx:value="a"/>
    <Container fx:value="b"/>
    <Container fx:value="c"/>
    <Container fx:value="d"/>
    <Container fx:value="e"/>
    <Container fx:value="f"/>
    
    <!-- fill readonly map property -->
    <map g="9.81">
        <hello>
            <Container fx:value="A"/>
        </hello>
        <answer>
            <Container fx:value=""/>
        </answer>
        <sample>
            <Sample>
                <!-- static setter-->
                <Container.number>
                    <Integer fx:value="33" />
                </Container.number>
            </Sample>
        </sample>
    </map>
</Sample>

if you want to reproduce, please indicate the source:
javafx – FXML and Controllers – Setting Properties - CodeDay