Search code examples
vue.jsjavafxfxml

Iterative rendering in FXML in non-table case


What the FXML equivalent for iterative rendering (v-for attribute) in below Vue-like markup?

<VBox>
    <HBox v-for="task of tasks">
        <Checkbox :checked="task.isComplete"></Checkbox>
        <VBox>
            <Label>{{ task.title }}</Label>
            <Label>{{ task.description }}</Label>
        </VBox>
    </HBox>
</VBox>

The desired displaying:

enter image description here

I prepared the below ObservableList:

ObservableList<Task> tasks = FXCollections.observableArrayList();
tasks.add(new Task("Wake up", "... and fly."));
tasks.add(new Task("Wash face", "... with cold water."));

Solution

  • ObservableList will react to new additions, you just have to display it programmatically in some way, for example using a custom component with ListView:

    package sample;
    
    import javafx.application.Application;
    import javafx.collections.FXCollections;
    import javafx.collections.ObservableList;
    import javafx.geometry.Pos;
    import javafx.scene.Scene;
    import javafx.scene.control.CheckBox;
    import javafx.scene.control.Label;
    import javafx.scene.control.ListCell;
    import javafx.scene.control.ListView;
    import javafx.scene.input.MouseEvent;
    import javafx.scene.layout.HBox;
    import javafx.scene.layout.Priority;
    import javafx.scene.layout.VBox;
    import javafx.scene.text.Text;
    import javafx.stage.Stage;
    
    public class Main extends Application {
    
        static class Task{
            String title, description;
            boolean done;
            Task(String title, String description){
                this.title = title;
                this.description = description;
            }
        }
    
        ListView<Task> list = new ListView<>();
        ObservableList<Task> data = FXCollections.observableArrayList(
                new Task("Wake up", "... and fly."),
                new Task("Wash face", "... with cold water.")
        );
    
        @Override
        public void start(Stage stage) {
            VBox box = new VBox();
            Scene scene = new Scene(box, 200, 200);
            stage.setScene(scene);
            stage.setTitle("ListViewSample");
            box.getChildren().addAll(list);
            VBox.setVgrow(list, Priority.ALWAYS);
            list.setItems(data);
            list.setCellFactory(list -> new TaskCell());
            stage.show();
        }
    
        static class TaskCell extends ListCell<Task> {
            @Override
            public void updateItem(Task task, boolean empty) {
                super.updateItem(task, empty);
                if (task != null) {
                    CheckBox done = new CheckBox();
                    done.addEventHandler(MouseEvent.MOUSE_CLICKED, event -> { task.done = !task.done; });
                    done.setSelected(task.done);
                    HBox cell = new HBox(
                        done,
                        new VBox(new Label(task.title), new Text(task.description))
                    );
                    cell.setAlignment(Pos.CENTER_LEFT);
                    setGraphic(cell);
                }
            }
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    }
    

    Result list

    See this article for more details.