Search code examples
javagwtbean-validationrequestfactorygwt-editors

GWT: ListEditor incorrectly tries to validate entities removed form the list


The problem is that ContactDocuments removed with editor.getList().remove(index) are still passed for validation making it impossible to save edited Contact. For example requestContext.save() will fail with onConstraintViolation if I add new ContactDocument with editor.getList().add() then immideatly remove it because of @NotNull violation on some fields.

I have simple relation: Contact has many ContactDocuments. I'm trying to edit Contact with request factory and editors framework. ContactDocuments is annotated with JSR-303 for basic validation.

My ListEditor for ContactDocuments collection:

public class ContactDocumentListEditor extends Composite implements IsEditor<ListEditor<ContactDocumentProxy, ContactDocumentEditor>>, HasRequestContext<List<ContactDocumentProxy>> {
private RequestContext requestContext;

interface ViewUiBinder extends UiBinder<Widget, ContactDocumentListEditor> {}
private final static ViewUiBinder uiBinder = GWT.create(ViewUiBinder.class);

private final ListEditor<ContactDocumentProxy, ContactDocumentEditor> editor = ListEditor.of(new DocumentEditorSource());

@UiField
VerticalPanel container;
@UiField
Button addContactDocumentButton;


@UiHandler("addContactDocumentButton")
void addContactDocumentButtonClick(ClickEvent event) {
    addNewContactDocument();
}


public ContactDocumentListEditor() {
    initWidget(uiBinder.createAndBindUi(this));
}

private class DocumentEditorSource extends EditorSource<ContactDocumentEditor> {
    @Override
    public ContactDocumentEditor create(final int index) {
        final ContactDocumentEditor documentEditor = new ContactDocumentEditor();
        documentEditor.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                removeDocumentEditor(index);
            }
        });
        container.insert(documentEditor, index);
        return documentEditor;
    }

    @Override
    public void dispose(ContactDocumentEditor subEditor) {
        container.remove(subEditor);
    }

    @Override
    public void setIndex(ContactDocumentEditor subEditor, int index) {
        container.insert(subEditor, index);
    }
}

@Override
public ListEditor<ContactDocumentProxy, ContactDocumentEditor> asEditor() {
    return editor;
}

@Override
public void setRequestContext(RequestContext ctx) {
    requestContext = ctx;
}

private void addNewContactDocument() {
    ContactDocumentProxy newDocument = requestContext.create(ContactDocumentProxy.class);
    editor.getList().add(newDocument);
}

private void removeDocumentEditor(int index) {
    editor.getList().remove(index);
}
}

My Presenter to drive it:

    final ContactRequestContext contactRequestContext =  contactRequestContextProvider.get();
    contactRequestContext.save(contact);
    driver.edit(contact, contactRequestContext);
    driver.flush().fire(new Receiver<Void>() {
        @Override
        public void onSuccess(Void response) {
            // success 
        }

        @Override
        public void onConstraintViolation(Set<ConstraintViolation<?>> violations) {
            for (ConstraintViolation violation : violations) {
                logger.info("Contact constraint violation: " + violation.getPropertyPath() + " = " + violation.getMessage());
            }
            driver.setConstraintViolations(violations);
        }
    });

How can I fix it?


Solution

  • Either defer the creation of the real proxy by using a double (create a class that implements your proxy interface, create an instance of that class rather than an RF proxy, and on or after flush create the real proxies to replace the fake ones; that way you'll never create a proxy that won't be sent to the server).

    Or if possible only validate your objects as part of others, never individually (don't validate a ContactDocument on its own, only as part of validating a Contact, using @Valid on the property containing the ContactDocuments): this can be accomplished using a ServiceLayerDecorator overriding the validate method, possibly combined with a @GroupSequence on either or both classes and the ServiceLayerDecorator validating using a specific group other than Default.

    Related issue: Cannot remove proxy from RequestContext editing context