Create dynamic form with vaadin 10

Hello everyone,
I used vaadin 10 and spring boot 2.04,
I want create dynamic form with polymer template, I have create abstract class for read dto and generate component with java reflect, but template model not accept generic class, can help me what solution for this ?

Thank all

My code:

CrudModel class

public interface CrudModel<T> extends TemplateModel {

    T getModelView();

    void setModelView(T model);

    HasText getTitle();

    void setBinder(Binder<T> binder);

    Binder<T> getBinder();
}

FormView.class

public abstract class FormView<E, T extends CrudModel<E>> extends PolymerTemplate<T> {

    private List<Class<?>> classOriginal = Arrays.asList(int.class, Integer.class, long.class, Long.class,
            double.class, Double.class, Short.class, boolean.class, Boolean.class, BigDecimal.class,
            BigInteger.class, String.class);

    private String fieldIdIgnore = "id";

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


    //Constructor for set up data
    public FormView() throws IllegalAccessException, IllegalArgumentException, InstantiationException {

    }

    public FormView(E dto) throws IllegalAccessException, IllegalArgumentException {
        if(null == dto) {
            throw new IllegalArgumentException("dto not null");
        }

        initComponents(dto, null, null, null);
        if(null == getModel().getBinder()) {
            initValidate(dto);
        }

        getModel().setModelView(dto);
    }


    private void initValidate(E dto) {
        Binder<E> binder = new BeanValidationBinder<E>((Class<E>) dto.getClass());
        getModel().setBinder(binder);
        if (null == components || components.isEmpty()) {
            return;
        }

        for (Map.Entry entry : components.entrySet()) {
            binder.forField((HasValue) entry.getValue());
        }

        binder.setBean(getModel().getModelView());
    }

    private void initComponents(Object dto, String modelName, String nameObject, Map<String, String> settings) throws IllegalAccessException {

        if (StringUtils.isBlank(modelName)) {
            modelName = "modelView";
        }

        Field[] classFields = dto.getClass().getDeclaredFields();
        for (Field field : classFields) {

            String nameField = field.getName();
            field.setAccessible(true);
            Object value = field.get(dto);
            Class<?> typeValue = field.getType();

            if (-1 == nameField.indexOf(fieldIdIgnore)) {
                continue;
            }

            String nameFieldModel = null;
            if (StringUtils.isNotBlank(nameObject)) {
                nameFieldModel = nameObject + "." + nameField;
            } else {
                nameFieldModel = nameField;
            }

            if (!classOriginal.contains(typeValue)) {
                initComponents(value, modelName, nameFieldModel, settings);
            } else {

                if (typeValue.isAssignableFrom(String.class)) {
                    TextField textField = new TextField();
                    textField.setLabel(nameField);
                    textField.setValue(value.toString());

                    getElement().appendChild(textField.getElement());
                    components.put(nameFieldModel, textField);
                }

                if (typeValue.isAssignableFrom(long.class) || typeValue.isAssignableFrom(Long.class)
                        || typeValue.isAssignableFrom(double.class) || typeValue.isAssignableFrom(Double.class)
                        || typeValue.isAssignableFrom(BigDecimal.class) || typeValue.isAssignableFrom(BigInteger.class)) {

                    TextField numberField = new TextField();
                    numberField.setLabel(nameField);
                    numberField.setPattern("\\d+(\\.\\d?\\d?)?$");
                    numberField.setPreventInvalidInput(true);
                    numberField.setValue(value.toString());

                    getElement().appendChild(numberField.getElement());
                    components.put(nameFieldModel, numberField);
                }
            }
        }
    }
}

You could try bypass the TemplateModel by using a combination of @Clientcallable and element.callFunction(), after all the TemplateModel is just persisting data between client-server.

Or you could implement the CrudModel. etc. MyNewEntityModel extends CrudModel

You don’t really want to persist a binder so get that out of the Model.

For each field you are persisting a simple string of number so could send an array or JSON array across.

Thank Hayden Dekker, I have changed class FormView to component, because when i use another class extend interface CrudModel, it will loop forever when create attribute in template model, so I remove it.

Oh cool and now TemplateModel works with Generics?

Nope, it loop forever when I create another interface and extend interface crud for new class.

Sorry I have changed to component and deleted interface crud so I can’t copy my error, I will explain with example:

I create new class RoleFormView extend FormView, RoleModel extend CrudModel, when I access link, it will create RoleModel and loop forever, jvm stop this and show error stack flow