Vaadin8 Use binder without pojo object

Hi,

what is the best way to use the Binder without a pojo class in vaadin8 ?

In my case, i do not use pojo because my crud are meta-data driven.

For instance:
Metadata: FieldName, FieldType, IsMandatory…
Data: LinkedHashMap of Key / Value

Bind a TextField with FieldName , the value get and set are from the LinkedHashMa get(FieldName) put(FieldName)

Thankx for the help

Hi, i did something like this, is it correct in the vaadin way ?

[code]
binder.bind(field,
new ValueProvider<Vertex, Object>() {
@Override
public String apply(Vertex vertex) {
return vertex.getProperty(crudField.getName());
}
},
new Setter<Vertex, Object>() {
@Override
public void accept(Vertex person, Object value) {
vertex.setProperty(crudField.getName(), value);
}

    });

[/code]Thanks

I’m interested in exactly the same thing. I thought I can bind to Map but its not working. For Vaadin 7 I found something like this https://vaadin.com/forum#!/thread/89760 … is there a way how to bind to non PJO strucutures (Maps etc ?) in Vaadin 8

In documentation there is PropertysetItem option https://vaadin.com/vaadin-documentation-portlet/framework/datamodel/datamodel-items.html … but my project cant find such class in Vaadin 8.

I’ve just bind a hashmap on x texfields (no validation, no metamodel…).

Here the code:

    Map<String,String> mapExample = new HashMap<String,String>();
    private VerticalLayout layout = new VerticalLayout(); 
    Binder<Map<String,String>> binder;
    
    /**
     * Create a textfield 
     * - add it to the layout
     * - bind the Map value to the textfield
     *  
     * @param code
     */
    private void createTextField(String code) {
        TextField tf = new TextField();
        tf.setWidth(250,Unit.PIXELS);
        layout.addComponent(tf);
        //bind element
        binder.forField(tf).bind(// getter
                list -> { return list.get(code);},
                //setter
                (list,fieldValue) -> {list.put(code, fieldValue);});

    }

    
    @Override
    public void enter(ViewChangeListener.ViewChangeEvent event) {
        logger.debug("enter");
        layout.removeAllComponents();
        // create a sample map
        mapExample = new HashMap<String,String>();
        mapExample.put("FIELD1", "VALUE1");
        mapExample.put("FIELD2", "VALUE2");
        mapExample.put("FIELD3", "VALUE3");

        // create the empty binder
        binder = new Binder<Map<String,String>>();
        for (Map.Entry<String, String> entry : mapExample.entrySet())
        { 
            createTextField(entry.getKey());
        }    
        // bind the sample bean to the list of textfields
        binder.setBean(mapExample);
        // test the result
        Button b = new Button("PUSH", e -> {
            for (Map.Entry<String, String> entry : mapExample.entrySet())
            {
                System.out.println("FIELD" +entry.getKey() + "-VALUE-"+entry.getValue());
            } 
        });
        layout.addComponent(b);
        this.addComponent(layout);
    }

Hi,

thanks for the code,

in my case, the field type is not known, so i have to refer to the field with a generic Object, also the typo of the data are not known.

The value of the map can be an Object, not String, than i have to be sure about the conversion of the value data type.

No one has done something similar ?

Thanks

Thanks for hint. One more question. How do you bind DateField . When I do it as below, I’m getting java.lang.ClassCastException: java.util.Date cannot be cast to java.time.temporal.Temporal error.

binder.forField(c) .bind(new ValueProvider<Map, Object>() {
@Override public Date apply(Map d) {
    return (Date) d.get(field.getId()); } },
  new Setter<Map, Object>() {
     @Override public void accept(Map d, Object value) { d.put(field.getId(), value); } }); }

Hi David, did you happen to find solution for this problem?

First of all, the key feature of Vaadin 8 is support for new Java 8 features. The API is supposed to be used with Java lambdas but not with inline classes. So,

binder.forField(c) .bind(new ValueProvider<Map, Object>() {
@Override public Date apply(Map d) {
    return (Date) d.get(field.getId()); } },
  new Setter<Map, Object>() {
     @Override public void accept(Map d, Object value) { d.put(field.getId(), value); } }); }

should look like

binder.forField(c).bind( data -> convertDateToString( data.get( field.getId() ),
                     (data, strValue) -> data.put(field.getId(), convertStringToDate(strValue));

Next level of the API is converters and validators.
You can use one of standard ones, your own ones, or chain any number of them together, like this:

binder.forField(dateField)
     .withConverter(new LocalDateToDateConverter())
     .bind( (data, value) -> data.get( field.getId() ), value-> data.put(field.getId(), value));

or

binder.forField(c) .withConverter(new AtoB()) .withConverter(new BtoString()) .bind( (data, value) -> data.get( field.getId() ), value-> data.put(field.getId(), value)); And, after all, Vaadin 8 uses new
LocalDate
and
LocalDateTime
classes instead of
Date
wich makes code more respectful of timezones. If your database keeps dates in
Date
objects, please convert the data first.

Unfortunately, you run into old part of the documentation specific to Vaadin 7. We will remove the pages, most probably.

Old, Vaadin7-compatible API still exists in Vaadin 8, in legacy packages (maven artifacts named vaadin-compatibility-server etc), but all that API is moved to com.vaadin.v7.* packages, and you can use the API if you still need that for some reason.


@Ilia
: I think we lost the track - the question starting the thread was how to dynamically bind data without POJO, when Field type, converter, validators etc. are not known at compile time. It is possible, but IMO it’s a huge step back compared with Vaadin7. For example, you replaced the Property interface by the HasValue interface, that can be parametrized. But you removed the getType() API method that I need if the types are not known at compile time. You replaced the API by Java 8 compiler features. But that’s not the same!

Looking at your examples in the documentation, everything I see is a TextField bound with a String POJO property. Nice, simple Hello World example, all types are known at compile time, lambdas work as expected, we are writing less code compared to Vaadin7. In such a simple case yes. But the real code is typically more complex. For example, I do not save anything if I need to declare repeatedly a converter for all my non-string fields. Years ago we were advised from Vaadin consultancy to use a ConverterFactory. Introduced in Vaadin7, the API was removed without any replacement in Vaadin8. Now we have in our framework more than 100 Converters (not so bad, the V8 Converters are very similar) and we need to write our own Converter Factory mechanism. We save nothing. Neither money nor time. And who knows, maybe in Vaadin 9 you will replace the Binder with a SuperBinder changing again everything.

Changing public API without a workaround is always a bad idea.