> Docs > Html Builder > Custom Builder

Custom Builder

HtmlPiece

HtmlPiece is the most basic type for html builders. An html document is a tree of HtmlPiece nodes.

The HtmlPiece interface has a single abstract method render(indent, out) which renders the piece as a sequence of chars as the piece would appear in an HTML document.

Builder Interface

An html builder interface contains nested subtypes of HtmlPiece, and builder methods that build such pieces. The builder methods should be default methods; the interface should have no abstract methods. App can access members of the interface simply by inheritance.

For example, the standard builder Html5 contains element types like Html5.DIV and builder methods like Html5._div(). Subclasses of Html5Doc (which implements Html5) access DIV and _div() through inheritance .

A custom builder interface follows the same pattern, for example

import static bayou.html.Html5.*;

public interface HtmlMisc
{
    public static final HtmlMisc misc = new HtmlMisc(){};

    default public A _url(String url) // do not use stand element names like _a(), _link()
    {
        return html5._a(url).href(url);
    }

This builder can be reused by different projects and teams. User code simply extends/implements HtmlMisc to access its members.

An instance of HtmlMisc is also provided (as HtmlMisc.misc) in case inheritance is not suitable in some situations. Actually here in the body of _url() method, we access Html5 builder methods through the Html5.html5 instance.

You may also consider static builder methods, if that's more convenient; e.g. CsrfToken._input().

The _url() example is just a syntax sugar to build a standard piece. Next, let's try creating some custom pieces.

Custom Piece

Let's start with a simple custom piece, Hex, which simply renders an integer in hex format. Not very exciting.

public interface HtmlMisc
{
    ...

    public class Hex implements HtmlPiece
    {
        int x;
        public Hex(int x)
        {
            this.x = x;
        }

        @Override
        public void render(int indent, Consumer<CharSequence> out)
        {
            out.accept("0x");
            out.accept(Integer.toHexString(x));
        }
    }

    default public Hex _hex(int x)
    {
        return ContextParent.add(new Hex(x));
    }

The builder method _hex() creates a Hex piece and adds it to the context parent.

public class TestHtml extends Html5Doc implements HtmlMisc
{
    ...
        _p("The value is ", _hex(47806));

A Bar Chart Example

As a more complex example, let's build a bar chart. The chart contains a list of integers; its color can be configured; its caption can contain arbitrary html pieces. The API should look like:

public class TestDoc extends Html5Doc implements HtmlMisc
{
    ...
        _barChart(100, 150, 200).color("red").caption(
            "Much Data", _br(),
            _url("http://example.com")
        );

We'll need a BarChart class, with a color(String) method, and a caption(Object...) method. It'll be rendered as a list of <div> elements with varying lengths.

    default public BarChart _barChart(int... data)
    {
        return ContextParent.add(new BarChart(data));
    }

    public class BarChart implements HtmlPiece
    {
        int[] data;
        public BarChart(int... data)
        {
            this.data = data;
        }

        String color="#777";
        public BarChart color(String color)
        {
            this.color = color;
            return this;
        }

        DIV caption = new DIV().style("text-align:center");
        public BarChart caption(Object... children)
        {
            caption.add(children);
            return this;
        }

        @Override
        public void render(int indent, Consumer<CharSequence> out)
        {
            DIV div = new DIV().style("border:1px solid black; width:300px;");
            for(int x : data)
                div.add( new DIV().add(x).style(String.format(
                    "width:%dpx; background-color:%s; margin:5px;", x, color)));
            div.add(caption);

            div.render(indent, out);
        }
    }