Nested forms with custom fields

Hi,

i am having a hard time getting to grips with data binding when building forms with custom fields.

I have a custom field (MailerSettingsField) which has three textfiles, bound to my Model.
This custom field is used as a part of a larger form, which at some point should contain a number of other, similar custom fields.

The text fields display the correct values.

Now i want to be able to do three things:

  1. When clicking the save button, the values from the text fields should be applied to the model.
  2. I want to be notified of changes to any text field, so that i can enable the save button.
  3. I want to be able to ask the custom field, whether it is modified.

Calling mailerSetingsField.commit() has no effect.
Calling mailerSettingsField.isModified() always returns false

I could overwrite commit() and isModified() of my custom field to achieve this, but i don’t think that’s the best way to go.

How can i achieve my goals?

public class SystemSettingsView extends VerticalLayout implements View {

    private final SystemSettings systemSettings;

    private BeanFieldGroup<SystemConfig> binder;

    @PropertyId(SystemConfig.MAILER_SETTINGS)
    private MailerSettingsField mailerSettingsField;

    private Button saveButton;

    @Autowired
    public SystemSettingsView(SystemSettings systemSettings) {
        this.systemSettings = systemSettings;
    }

    @Override
    public void enter(ViewChangeEvent event) {
        saveButton = new Button("Save", FontAwesome.SAVE);
        saveButton.addClickListener(e -> save());
        saveButton.setEnabled(false);

        mailerSettingsField = new MailerSettingsField("Mailer Settings");

        binder = new BeanFieldGroup<>(SystemConfig.class);
        binder.bindMemberFields(this);
        binder.setItemDataSource(systemSettings.getSystemConfig());

        addComponent(new VerticalLayout(mailerSettingsField, saveButton));
    }

    private void save() {
        mailerSetingsForm.commit();
    }
}

public class MailerSettingsField extends CustomField<MailerSettings> {

    @PropertyId(MailerSettings.MAILER_HOST)
    private final TextField host = new TextField("Host");

    @PropertyId(MailerSettings.MAILER_SENDER_NAME)
    private final TextField senderName = new TextField("Sender Name");

    @PropertyId(MailerSettings.MAILER_SENDER_ADDRESS)
    private final TextField senderAddress = new TextField("Sender Address");

    private final FormLayout content;
    private final BeanFieldGroup<MailerSettings> binder;

    public MailerSettingsField(String caption) {
        Label label = new Label(caption);

        content = new FormLayout(label);
        content.setWidth("100%");

        content.addComponents(host, senderName, senderAddress);

        binder = new BeanFieldGroup<>(MailerSettings.class);
        binder.bindMemberFields(this);
    }

    @Override
    protected void setInternalValue(MailerSettings settings) {
        super.setInternalValue(settings);
        binder.setItemDataSource(settings);
    }

    @Override
    protected Component initContent() {
        return content;
    }

    @Override
    public Class<MailerSettings> getType() {
        return MailerSettings.class;
    }
}

I did not try your code, but i think the problem is, that you don’t commit the field-group of MailerSettingsField.
So you can set binder.setBuffered(false) (what i would recommend) or you can overwrite the commit-function of MailerSettingsField:

public void commit() {
binder.commit();
}

and to enable/disable the save-button, just use ValueChangeListeners on the textfields and the MailerSettingsField itself