Search code examples
javaconvertersvaadinvaadin7

Clear a FieldGroup in Vaadin 7


How does one clear the fields in a FieldGroup in a Vaadin 7 app?

When I call FieldGroup::setItemDataSource and pass null, I get the text "null" appearing in TextField objects backed by various data types (Number, java.util.Date). I can understand null values displaying "null" as a String representation. But why do those "null" strings not appear by default? By default I am seeing empty TextField strings in all fields.

For example, consider the example app below. In the following screenshots we:

  1. Launch the app. User takes no action.
    All fields blank, no strings, no "null" text.
  2. User clicks the "Show Bernard" button.
  3. User clicks the "Show No One" button. This button calls FieldGroup::setItemDataSource( null ), passing null.
    Both the number and date fields have "null" text displayed. Why now and not at launch?
    How can I get back to blank values?

App launch

App launches. User takes no action.

User clicks "Show Bernard" button

User clicks "Show Bernard" button.

User clicks "Show No One" button

User clicks "Show No One" button.

Here is the complete single-file Vaadin 7.5.3 app in Java 8 (using Lambda syntax).

package com.example.gridexample;

import javax.servlet.annotation.WebServlet;

import com.vaadin.annotations.Theme;
import com.vaadin.annotations.VaadinServletConfiguration;
import com.vaadin.annotations.Widgetset;
import com.vaadin.data.fieldgroup.FieldGroup;
import com.vaadin.data.util.ObjectProperty;
import com.vaadin.data.util.PropertysetItem;
import com.vaadin.data.util.converter.StringToDateConverter;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinServlet;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Label;
import com.vaadin.ui.TextField;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Date;

/**
 *
 */
@Theme ( "mytheme" )
@Widgetset ( "com.example.gridexample.MyAppWidgetset" )
public class MyUI extends UI
{

    PropertysetItem itemAaron = null;
    PropertysetItem itemBernard = null;
    TextField nameField = null;
    TextField ageField = null;
    TextField dateOfBirthField = null;
    FieldGroup fieldGroup = null;
    ZoneId zone = ZoneId.systemDefault(); // Or ZoneId.of( "America/Montreal" ) ;

    @Override
    protected void init ( VaadinRequest vaadinRequest ) {
        final VerticalLayout layout = new VerticalLayout();
        layout.setMargin( true );
        layout.setSpacing( true );
        setContent( layout );

        Button buttonAaron = new Button( "Show Aaron" );
        buttonAaron.addClickListener( ( ClickEvent event ) -> {
            showAaron();
        } );
        layout.addComponent( buttonAaron );

        Button buttonBernard = new Button( "Show Bernard" );
        buttonBernard.addClickListener( ( ClickEvent event ) -> {
            showBernard();
        } );
        layout.addComponent( buttonBernard );

        Button buttonNoOne = new Button( "Show No One" );
        buttonNoOne.addClickListener( ( ClickEvent event ) -> {
            showNoOne();
        } );
        layout.addComponent( buttonNoOne );

        this.itemAaron = new PropertysetItem();
        this.itemAaron.addItemProperty( "name" , new ObjectProperty<>( "Aaron" ) );
        this.itemAaron.addItemProperty( "age" , new ObjectProperty<>( 42 ) );
        this.itemAaron.addItemProperty( "dateOfBirth" , new ObjectProperty<>( Date.from( ZonedDateTime.now( this.zone ).minusYears( 42 ).toInstant() ) ) );

        this.itemBernard = new PropertysetItem();
        this.itemBernard.addItemProperty( "name" , new ObjectProperty<>( "Bernard" ) );
        this.itemBernard.addItemProperty( "age" , new ObjectProperty<>( 24 ) );
        this.itemBernard.addItemProperty( "dateOfBirth" , new ObjectProperty<>( Date.from( ZonedDateTime.now( this.zone ).minusYears( 24 ).toInstant() ) ) );

        this.nameField = new TextField( "Name:" );
        this.ageField = new TextField( "Age:" );
        this.dateOfBirthField = new TextField( "Date Of Birth:" );

        // Customize the Converter assigned to our date-time field. A StringToDateConverter object is assigned by default.
        //this.dateOfBirthField.setConverter( new StringToDateConverter() );  // Actually the default.
        //  this.dateOfBirthField.setConverter( new SpecialStringToDateConverter() );
        layout.addComponent( this.nameField );
        layout.addComponent( this.ageField );
        layout.addComponent( this.dateOfBirthField );

        this.fieldGroup = new FieldGroup();
        this.fieldGroup.bind( this.nameField , "name" );
        this.fieldGroup.bind( this.ageField , "age" );
        this.fieldGroup.bind( this.dateOfBirthField , "dateOfBirth" );
        this.fieldGroup.setReadOnly( true );

    }

    private void showAaron () {
        this.fieldGroup.setItemDataSource( this.itemAaron );
    }

    private void showBernard () {
        this.fieldGroup.setItemDataSource( this.itemBernard );
    }

    private void showNoOne () {
        this.fieldGroup.setItemDataSource( null );
    }

    @WebServlet ( urlPatterns = "/*" , name = "MyUIServlet" , asyncSupported = true )
    @VaadinServletConfiguration ( ui = MyUI.class , productionMode = false )
    public static class MyUIServlet extends VaadinServlet
    {
    }

}

Solution

  • Before click at any button (initial state) those line are executed:

    TextField nameField = null;
    //...
    this.nameField = new TextField( "Name:" );
    

    Documentation says:

    public TextField(java.lang.String caption)

    Constructs an empty TextField with given caption.

    So all textfields in your application has empty values. But... After that you use:

    this.fieldGroup.bind( this.nameField , "name" );
    

    Should the value be null? Again documentation for [bind()](https://vaadin.com/api/com/vaadin/data/fieldgroup/FieldGroup.html#bind(com.vaadin.ui.Field, java.lang.Object)) says:

    Binds the field with the given propertyId from the current item. If an item has not been set then the binding is postponed until the item is set using setItemDataSource(Item).

    So you must use this method to see bind effects.

    You asked: But why do those "null" strings not appear by default?

    And I hope that my explanation is clear for you, that initially each TextField has empty value. And after call bind() function, it affect view only after setItemDataSource(Item), but you call this function much later in button's clickListener. So initially it is fine, that all fields has empty values.