Vaadin 24.4.11 LitRenderer with dynamic text and styling

Hi,

I have a POJO with a Status enum. I need to display the status as a badge in a Grid.
I managed to successfully do it using the ComponentRenderer.

public static Renderer<BookRequest> getStatusRenderer() {
        return new ComponentRenderer<Span, BookRequest>(
                new SerializableFunction<BookRequest, Span>() {
                    @Override
                    public Span apply(BookRequest bookRequest) {
                        var span = new Span(bookRequest.getStatus().toString());
                        switch (bookRequest.getStatus()) {
                            case NEW -> span.getElement().getThemeList().add("badge contrast");
                            case PROCESSING -> span.getElement().getThemeList().add("badge");
                            case APPROVED -> span.getElement().getThemeList().add("badge success");
                            case REJECTED -> span.getElement().getThemeList().add("badge error");
                            default -> span.getElement().getThemeList().add("badge pill");
                        }

                        return span;
                    }
                }
        );
    }

I read that ComponentRenderers are not really efficient and the correct way to do it is using LitRenderer.
So in the LitRenderer I need to display the enum string and apply the theme.
Problem is the LitRenderer can manipulate only one value.

I am banging my head on the wall for 2 days and just can’t figure out how to do it.
Can someone please help point me in the right direction?

Thank you.

It depends. I would suggest defining a performance budget, measure where you are relative to that and to use a more complex approach only if it’s actually necessary.

You can call withProperty multiple times to define multiple values that are sent to the client-side renderer.

It depends. I would suggest defining a performance budget, measure where you are relative to that and to use a more complex approach only if it’s actually necessary.

I get your point. I am just trying to get it right the first time instead of going back and fixing it later, when (hopefully) my data grows.

You can call withProperty multiple times to define multiple values that are sent to the client-side renderer.

I was not aware of this. Will definitely give it a try.

Thank you for your help!

Note that performance in this case is based on the amount of visibile data (plus a fixed scroll-ahead buffer) and not the total number of rows in the database. This means that you can get a good understanding of the performance with only something like 100 rows in the database.

1 Like

I was under the impression that all the data is loaded in one shot.
I have been following the Vaadin tutorial and the Grid examples talk about lazy loading. Now I realise I have clearly misunderstood them.
Thanks for the clarification.

As for my original problem, I did get it working by calling .withProperty twice, once for the style, and once for the value as you had suggested. Unfortunately setting the css to make it look like a vaadin badge is more trouble than I had anticipated.
However I did use it in another place, where I wanted some values bold and others plain based on some business logic

Considering all the new things I have learnt in this thread, I think I will go with the ComponentRenderer.
I am marking your original reply as the accepted solution.

Again, thank you so much!

This is probably be because there are different types of lazy loading. Grid is always lazy about what data is sent to the client and rendered regardless of whether application logic has eagerly loaded all the rows into memory.

1 Like

Here is an example of how you can do it with a LitRenderer vs ComponentRenderer.

In term of performance, it should be very likely similar.
You can see a big performance gap between LitRenderer and ComponentRenderer when the ComponentRenderer is creating a lot of components. In your case, it’s only one Span.

package com.example.application.views;

import com.vaadin.flow.component.Html;
import com.vaadin.flow.component.dependency.JsModule;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.html.Span;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.data.renderer.ComponentRenderer;
import com.vaadin.flow.data.renderer.LitRenderer;
import com.vaadin.flow.data.renderer.Renderer;
import com.vaadin.flow.function.SerializableFunction;
import com.vaadin.flow.router.Route;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

@Route("lit-grid")
public class GridView extends VerticalLayout {

    private List<BookRequest> bookRequests = new ArrayList<>();

    public GridView() {
        Grid<BookRequest> grid = new Grid<>();
        grid.addColumn(LitRenderer.<BookRequest>of("""
                        <span theme=${item.themename}>${item.label}</span>
                        """)
                .withProperty("label",
                        bookRequest -> bookRequest.getStatus().toString())
                .withProperty("themename",
                        bookRequest -> switch (bookRequest.getStatus()) {
                            case NEW -> "badge contrast";
                            case PROCESSING -> "badge";
                            case APPROVED -> "badge success";
                            case REJECTED -> "badge error";
                        })).setHeader("status Lit");
        grid.addColumn(new ComponentRenderer<>(bookRequest -> {
            var span = new Span(bookRequest.getStatus().toString());
            switch (bookRequest.getStatus()) {
                case NEW -> span.getElement().getThemeList().add("badge contrast");
                case PROCESSING -> span.getElement().getThemeList().add("badge");
                case APPROVED -> span.getElement().getThemeList().add("badge success");
                case REJECTED -> span.getElement().getThemeList().add("badge error");
                default -> span.getElement().getThemeList().add("badge pill");
            }

            return span;
        })).setHeader("status");
        add(grid);
        fillBookRequests();
        grid.setItems(bookRequests);
        grid.setSizeFull();
        setSizeFull();
    }

    private void fillBookRequests() {
        for (int i = 0; i < 100; i++) {

            bookRequests.add(new BookRequest(getRandomStatus()));
        }
    }

    private BookStatus getRandomStatus() {
        return BookStatus.values()[new Random().nextInt(BookStatus.values().length)];
    }



    public static Renderer<BookRequest> getStatusRenderer() {
        return new ComponentRenderer<Span, BookRequest>(
                new SerializableFunction<BookRequest, Span>() {
                    @Override
                    public Span apply(BookRequest bookRequest) {
                        var span = new Span(bookRequest.getStatus().toString());
                        switch (bookRequest.getStatus()) {
                            case NEW -> span.getElement().getThemeList().add("badge contrast");
                            case PROCESSING -> span.getElement().getThemeList().add("badge");
                            case APPROVED -> span.getElement().getThemeList().add("badge success");
                            case REJECTED -> span.getElement().getThemeList().add("badge error");
                            default -> span.getElement().getThemeList().add("badge pill");
                        }

                        return span;
                    }
                }
        );
    }
    public class BookRequest {
        private BookStatus status;


        public BookRequest(BookStatus status) {
            this.status = status;
        }

        public BookStatus getStatus() {
            return status;
        }
    }

    public enum BookStatus {
        NEW, PROCESSING, APPROVED, REJECTED;
    }
}

2 Likes