TemplateRenderer for a vaadin-button with a variable theme

I’m creating a TemplateRendererFactory, and having problems making a <vaadin-button> with a VaadinIcon prefix. I successfully implemented one that never has a Button label (button with only an icon). This is the working implementation for that:

public static <SOURCE> TemplateRenderer<SOURCE> iconButton(ValueProvider<SOURCE, VaadinIcon> iconProvider, SerializableConsumer<SOURCE> clickEvent){
        String id = UUID.randomUUID().toString();
        return TemplateRenderer.<SOURCE> of("<vaadin-button on-click='buttonClicked' theme='icon' class='template-renderer-button'><iron-icon icon='vaadin:[[item.iconType_"+id+"]
]' slot='prefix'></iron-icon></vaadin-button>")
                .withProperty("iconType_"+id,   item -> iconProvider.apply(item).name().toLowerCase(Locale.ENGLISH).replace('_', '-'))
                .withEventHandler("buttonClicked", clickEvent);
    }

An important aspect of that button is the theme='icon' which takes care of reducing excess space/margins so that the icon is centered nicely in the button.
So far so good.


Now I want to have also a labelProvider, and I need to set the button’s theme to icon if the provided label is empty.
This is my implementation, which I would expect to work:

public static <SOURCE> TemplateRenderer<SOURCE> iconButton(ValueProvider<SOURCE, String> labelProvider, ValueProvider<SOURCE, VaadinIcon> iconProvider, SerializableConsumer<SOURCE> clickEvent){
        String id = UUID.randomUUID().toString();
        return TemplateRenderer.<SOURCE> of("<vaadin-button on-click='buttonClicked' theme='[[item.theme_"+id+"]
]' class='template-renderer-button'><iron-icon icon='vaadin:[[item.iconType_"+id+"]
]' slot='prefix'></iron-icon>[[item.label_"+id+"]
]</vaadin-button>")
                .withProperty("theme_"+id,      item -> Strings.isEmpty(labelProvider.apply(item)) ? "icon" : "")
                .withProperty("label_"+id,      item -> {
                    String labelValue = labelProvider.apply(item);
                    return Strings.isEmpty(labelValue) ? "" : labelValue;
                })
                .withProperty("iconType_"+id,   item -> iconProvider.apply(item).name().toLowerCase(Locale.ENGLISH).replace('_', '-'))
                .withEventHandler("buttonClicked", clickEvent);
    }

If the provided label is empty, the rendered button should have the attribute theme='icon', but it does not.
Why does this not work? Any help or pointers are appreciated.


more code for reproduction, and a screenshot of the result:

@Route(value = "Test")
public class TestView extends VerticalLayout {
    public TestView() {
        ValueProvider<String, VaadinIcon> iconProvider = item -> VaadinIcon.CHECK_CIRCLE;
        SerializableConsumer<String> notifyString = item -> Notification.show(item);

        Grid<String> templateRendererGrid = new Grid<>(String.class, false);
		
		// theme='icon' is applied
        templateRendererGrid.addColumn(TemplateRendererFactory.iconButton(iconProvider, notifyString)).setHeader("no-label API"); 
		
		// no theme is applied (correct!)
        templateRendererGrid.addColumn(TemplateRendererFactory.iconButton(item -> "Label", iconProvider, notifyString)).setHeader("Provided Label"); 
		
		// no theme is applied (wrong!)
        templateRendererGrid.addColumn(TemplateRendererFactory.iconButton(item -> null, iconProvider, notifyString)).setHeader("Provided Label: null"); 

        templateRendererGrid.setItems("a", "b", "c");
        templateRendererGrid.setHeightByRows(true);
        templateRendererGrid.getColumns().forEach(row -> row.setResizable(true));
        add(templateRendererGrid);
    }
}

![grid result]
(https://i.imgur.com/uU3S1uR.png)

I found the solution. The problem is that theme is an atrribute and not a property, and therefore needs to be defined with a $ suffix in the attribute-name. (only if the attribute value is being parsed using withProperty(..))

These work:
<vaadin-button theme$='[[item.theme] ]'>...</vaadin-button>
<vaadin-button theme='icon'>...</vaadin-button>

This doesn’t work:
<vaadin-button theme='[[item.theme] ]'>...</vaadin-button>

This was very arduous to find out, as there is absolutely no documentation on Template Renderers, except for the [basic introduction on the Docs for Grid]
(https://vaadin.com/docs/v14/flow/components/tutorial-flow-grid.html#using-template-renderers). I really hope the documentation on that gets massively improved :wink:
I only found it with the hardcoded warning message at TemplateRenderer::warnIfClassOrStyleWithoutDollar, which only is about style and class attributes, but I figured this would apply to other attributes as well.

There is a (old) ticket about this: https://github.com/vaadin/flow-and-components-documentation/issues/845

I’ve added a comment and also link to the limitation (can’t use the same property name in two templaterenderer)