2

I was wondering if it was possible, using FXML, to do templating like it is possible with Twig in PHP (and surely lots of other template motors)

Using Twig you would have a view like this :

layout:

<html>
<head>
    <title>{% block title %}My Website{% endblock %}</title>
</head>

<body>
{% block body %}{% endblock %}
</body>
</html>

some_page:

{% extends "layout" }
{% block title %}{{ parent() }} - Some page{% endblock %}
{% block body %}
    <div>Some content</div>
{% endblock %}

This would render like this in the browser:

<html>
<head>
    <title>My Website - Some page</title>
</head>

<body>
    <div>Some content</div>
</body>
</html>

My question is: is there something like this possible with FXML. So instead of HTML we would have standard FXML and some special tags to be redefined in child FXML files.

I now that there is an inclusion mechanism but that's not what I'm searching. Inclusion suppose that you necessarily have to redefine the "blocks", even with empty files. What I want is an inheritance mechanism.

Is it possible using FXML?

François Dupire
  • 585
  • 3
  • 11
  • 32
  • 1
    Nothing is built in like that, but note that you can load FXML [specifying only an `InputStream`](http://docs.oracle.com/javase/8/javafx/api/javafx/fxml/FXMLLoader.html#load-java.io.InputStream-). So, while I've never tried it, and it would probably take quite a bit of work at first to figure out how to do it, it should be possible to use any general-purpose Java templating library (such as [Tiles](https://tiles.apache.org/)) to generate FXML on the fly. – James_D Jun 13 '16 at 12:39
  • Sort of related: http://stackoverflow.com/questions/24321871/building-javafx-ui-dynamically-on-the-fly/24329725#24329725 – James_D Jun 13 '16 at 12:53
  • Thank you for your answer James_D. Unfortunately it seems that there is nothing pre-built in JavaFX to handle such a mechanism. That's unfortunate as it's really a nice feature. – François Dupire Jun 14 '16 at 12:53
  • Also see this question, which maybe provides another way to do something along these lines: http://stackoverflow.com/questions/29042109/javafx8-list-bindings-similar-to-xaml – James_D Jun 16 '16 at 17:22
  • I've found some way to achieve what I want. There is a library named pebble (http://www.mitchellbosecke.com/pebble/home) that replicate the features of Twig in Java. And it consumes any kind of file (if the markup of the file doesn't override Pebble markup I assume). The library gives you then the rendered content and you can write it in a temp file you can use for FXML. I'll write a more detailed post whenever I try this a bit much. – François Dupire Jun 17 '16 at 07:34
  • Hi @FrançoisDupire Do you plan to use pebble at runtime or as a helper library to generate FXML files at development or build time? – FuryFart Jun 17 '16 at 17:53
  • Hi @SurprisedCoconut. As you can see in the answer I just posted I used it at runtime. – François Dupire Jun 20 '16 at 06:58

1 Answers1

1

Here is how I solved my problem using Pebble. The following example served me as a proof of concept before using Pebble in my real project. I kept it very simple but it did the job of proving what I wanted to achieve was possible.

Layout.pebble.fxml:

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

<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>
<?import javafx.scene.control.*?>

<BorderPane id="page" prefWidth="1280.0" prefHeight="900.0" xmlns:fx="http://javafx.com/fxml">
    <top>
        <MenuBar BorderPane.alignment="CENTER">
            <menus>
                <Menu mnemonicParsing="false" text="File">
                    <items>
                        <MenuItem mnemonicParsing="false" onAction="#exitAction" text="Exit" />
                    </items>
                </Menu>

                <Menu mnemonicParsing="false" text="Help">
                    <items>
                        <MenuItem mnemonicParsing="false" text="About" />
                    </items>
                </Menu>
            </menus>
        </MenuBar>
    </top>

    <center>
        {% block body %}{% endblock %}
    </center>
</BorderPane>

Index.pebble.fxml:

{% extends "templates/Layout.pebble.fxml" %}

{% block body %}
<VBox xmlns:fx="http://javafx.com/fxml/1">
    <Text text="Test" />
</VBox>
{% endblock %}

App.java:

package be.dupirefr.pebblefx;

import java.io.File;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

import com.mitchellbosecke.pebble.PebbleEngine;
import com.mitchellbosecke.pebble.template.PebbleTemplate;

import be.dupirefr.pebblefx.controllers.DumbController;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class App extends Application {
    public static void main(String args[]) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        PebbleEngine engine = new PebbleEngine.Builder().build();
        PebbleTemplate compiledTemplate = engine.getTemplate("templates/Index.pebble.fxml");

        Map<String, Object> context = new HashMap<>();

        File file = File.createTempFile("views/Index", "fxml");
        file.deleteOnExit();

        PrintWriter writer = new PrintWriter(file);
        compiledTemplate.evaluate(writer, context);
        writer.close();

        primaryStage.setTitle("Pebble test");

        FXMLLoader loader = new FXMLLoader();

        loader.setController(new DumbController());
        loader.setLocation(file.toURI().toURL());

        BorderPane pane = loader.load();
        Scene scene = new Scene(pane);

        primaryStage.setScene(scene);
        primaryStage.show();
    }
}

The process generated this file :

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

<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>
<?import javafx.scene.control.*?>

<BorderPane id="page" prefWidth="1280.0" prefHeight="900.0"
    xmlns:fx="http://javafx.com/fxml">
    <top>
        <MenuBar BorderPane.alignment="CENTER">
            <menus>
                <Menu mnemonicParsing="false" text="File">
                    <items>
                        <MenuItem mnemonicParsing="false" onAction="#exitAction"
                            text="Exit" />
                    </items>
                </Menu>

                <Menu mnemonicParsing="false" text="Help">
                    <items>
                        <MenuItem mnemonicParsing="false" text="About" />
                    </items>
                </Menu>
            </menus>
        </MenuBar>
    </top>

    <center>
        <VBox xmlns:fx="http://javafx.com/fxml/1">
            <Text text="Test" />
        </VBox>
    </center>
</BorderPane>

And it is successfully used by the FXMLLoader.

Of course you can use other features of Pebble, but what I wanted to see was if it was going to work with any kind of file and if the FXMLLoader will take the temporary file as input.

François Dupire
  • 585
  • 3
  • 11
  • 32