Documentation

Documentation versions (currently viewing)

Custom Flow Components in Hilla

Embedding Custom Flow components in Hilla views.

Custom Flow components can be embedded in a Hilla view by implementing a WebComponentExporter and using it in the view. A WebComponentExporter can target any Flow Component.

Below is an example for a component that follows this:

public class CustomComponent extends Div {

    public CustomComponent(@Autowired GreetService service) {
        Button button = new Button("Say hello", e -> {
            Notification.show("Hello!");
        });

        add(button);
    }
}

This would then be embedded into a WebComponent like so:

public class MyFlowComponentExporter
        extends WebComponentExporter<CustomComponent> {

    public static final String TAG = "my-flow-component";

    public MyFlowComponentExporter() {
        super(TAG);
    }

    @Override
    protected void configureInstance(WebComponent<CustomComponent> webComponent,
                                     CustomComponent component) {
    }
}

For more information, see Creating an Embedded Vaadin Application.

Note
The WebComponentExporter needs to have a public no-argument constructor. Otherwise, it won’t be instantiated or generated.

To add the exported WebComponent to a Hilla view, import createWebComponent from Flow and create a React.DOMElement for the WebComponent TAG. Then use the element inside the view layout like this:

import { VerticalLayout } from "@vaadin/react-components/VerticalLayout";
import { createWebComponent } from "Frontend/generated/flow/Flow";

function MyFlowComponent() {
  return createWebComponent("my-flow-component");
}

export default function HillaView() {
  return (
    <>
      <VerticalLayout className={'centered-content'}>
        <h3>Hilla View</h3>
        <MyFlowComponent/>
      </VerticalLayout>
    </>
  );
}

Using Attributes

Adding attributes for the element can be done by giving a Properties object with string value pairs to the createWebComponent. The Properties is defined as [key: string]: string;.

This can be used when the WebComponent exposes properties to the client.

import { VerticalLayout } from "@vaadin/react-components/VerticalLayout";
import { createWebComponent } from "Frontend/generated/flow/Flow";
import React from "react";

function MyFlowComponent() {
  // Create element with property hellomsg
  return createWebComponent("my-flow-component", {
    hellomsg: 'Hi from the client!'
  });
}

export default function HillaView() {
  return (
    <>
      <VerticalLayout className={'centered-content'}>
        <h3>Hilla View</h3>
        <MyFlowComponent/>
      </VerticalLayout>
    </>
  );
}

The properties can also be linked to the element attributes using a custom properties type:

import { VerticalLayout } from "@vaadin/react-components/VerticalLayout";
import { createWebComponent } from "Frontend/generated/flow/Flow";
import React from "react";

type MyProperties = {
  hellomsg: string
}

function MyFlowComponent(props: MyProperties) {
  // Create element with property hellomsg
  return createWebComponent("my-flow-component", {
    hellomsg: props.hellomsg
  });
}

export default function HillaView() {
  return (
    <>
      <VerticalLayout className={'centered-content'}>
        <h3>Hilla View</h3>
        <MyFlowComponent hellomsg={'Hi from the client!'}/>
      </VerticalLayout>
    </>
  );
}

In this way, changing the attribute will also update the WebComponent property value.

The corresponding server-side code for the attribute component is made as the following example shows:

public class MyFlowComponentExporter
        extends WebComponentExporter<CustomComponent> {

    public static final String TAG = "my-flow-component";

    public MyFlowComponentExporter() {
        super(TAG);
        addProperty("hellomsg", "Hello!")
            .onChange(CustomComponent::setHelloMessage);
    }

    @Override
    protected void configureInstance(WebComponent<CustomComponent> webComponent,
                                     CustomComponent component) {
    }
}
public class CustomComponent extends Div {
    String helloMessage;

    public CustomComponent(@Autowired GreetService service) {
        Button button = new Button("Say hello", e -> {
            Notification.show(helloMessage);
        });

        add(button);
    }

    public void setHelloMessage(String helloMessage) {
        this.helloMessage = helloMessage;

    }
}

Onload Event for WebComponent

Loading the WebComponent script can take some time, depending on the network. Therefore, it might be prudent to show a loading indicator so the user knows to wait.

It’s possible to listen to the onload event for the WebComponent script so that the loading element can be removed when the script is finished loading.

The createWebComponent accepts an onload callback function as the third parameter. An onerror callback can be set as the fourth parameter. However, if it’s not given, there will be an error logged into the console with the script tag to show which WebComponent script failed to load.

import { VerticalLayout } from "@vaadin/react-components/VerticalLayout";
import { createWebComponent } from "Frontend/generated/flow/Flow";
import React from "react";

type MyProperties = {
  hellomsg: string
}

function MyFlowComponent(props: MyProperties) {
  // Create element with property hellomsg
  return createWebComponent("my-flow-component",
    undefined,
    () => document.getElementById("loading")?.remove()
  );
}

export default function HillaView() {
  return (
    <>
      <VerticalLayout className={'centered-content'}>
        <h3>Hilla View</h3>
        <!-- Placeholder element for MyFlowComponent script loading -->
        <div id={"loading"}>Loading script...</div>
        <MyFlowComponent hellomsg={'Hi from the client!'}/>
      </VerticalLayout>
    </>
  );
}

920dc03d-5eb4-4826-8934-4416b58a9a3e