Double-click seems to disrupt Wicket-Spring injection

Our application uses a Wicket front-end, with Spring injection to load our DOAs and manage transactions.

We have discovered that several of our users are double clicking on links/buttons and this is somehow disrupting the Spring injection, so that subsequent calls to whateverDao.doStuff(obj) throw N.P.E. This application runs on our client's internal network, and for now, we have politely requested that the client spread the word amongst their team that single clicks work on all features. But it is apparent that this is becoming a problem for them.

A common use case involves a "Search" screen showing a list of all Foo objects currently in the system, which can be filtered via search parameters if desired, and when an item is clicked the user is taken to a detail page for that specific Foo, initially in a read-only mode. Next, the user may click an "Edit" button in the corner to switch to edit mode. Then, the user might make some changes and click "Save" (or possibly click "Delete" to remove the item.)

This scenario involves DAO calls at up to three steps: 1. On the search page, when the item is clicked, to load basic details of that item. 2. On the detail page in read-only mode, when edit is clicked, to load complete details of that item. 3a. On the detail page in edit mode, when save is clicked, to persist the changes. 3b. On the detail page in edit mode, when delete is clicked, to delelte.

In any of these cases, if the user double clicked on the previous step, the next step produces the error. The reproducablitiy is about 33% with some variations between browsers and OSs.

Any insight on preventing this?


In the samples below, BasePage is our custom extension of Wicket's WebPage containing our menus and other common page elements, and PageType is an enumeration of CREATE, EDIT, and READ_ONLY details.

Sample code for search page (Java shown, HTML is what you expect):

import org.apache.wicket.spring.injection.annot.SpringBean;
// and other imports

public class FooManagerPage extends BasePage {

    @SpringBean
    private transient FooDao fooDao;

    public FooManagerPage() {
        SortableDataProvider<Foo> provider = new SortableDataProvider<Foo>(fooDao);

        add(new FeedbackPanel("feedback");

        final Form<Foo> searchFooForm = new Form<Foo>("searchFooForm",
            new CompoundPropertyModel<Foo>(new Foo()));

        // the form's search parameter's go here
        // with a 'search' button that filters table below

        add(searchFooForm)

        List<IColumn<Foo>> columns = new ArrayList<IColumn<Foo>>();
        columns.add(new PropertyColumn<Foo>(Model.of("Name"), "name", "name"));
        // a couple other columns here

        DataTable fooTable = new AjaxFallbackDefaultDataTable<Foo>("fooTable", columns, provider, 10){
            @Override
            protected Item<Foo> newRowItem(String id, int index, final IModel<Foo> model){
                Item<Foo> item = super.newRowItem(id, index, model);
                item.add(new AjaxEventBehavior ("onclick") {
                    @Override
                    protected void onEvent(AjaxRequestTarget target) {
                        Foo foo = fooDao.load(model.getObject().getId());
                        setResponsePage(new FooViewPage(foo, PageType.READ_ONLY));
                    }
                }); 
                return item;
            }
        };

        add(fooTable);
    }
}

Sample code for view page (Java shown, HTML is what you expect)::

// several imports, including Spring Bean
public class FooFormPage extends BasePage {

    @SpringBean
    private transient fooDao fooDao;

    public FooFormPage(final Foo foo, PageType type) {
        Form<Foo> fooForm = new Form<Foo>("fooForm",
            new CompoundPropertyModel<Foo>(foo));

        // all of Foo's input elements go here
        // are enabled or disabled and sometimes invisible based on PageType

        add(fooForm);

        SubmitLink submitButton = new SubmitLink("save", fooForm){
            @Override
            public void onSubmit() {
                super.onSubmit();
                //***** A double click on the Edit button can cause fooDao to be N.P.E. here *****
                fooDao.save(createInitiativeForm.getModelObject().getId());
                changePageType(PageType.VIEW, createFooForm.getModelObject());
            }
        };
        add(submitButton);

        AjaxLink<Void> editButton = new AjaxLink<Void>("edit"){
            @Override
            public void onClick(AjaxRequestTarget target) {
                // reload the item from DB
                //***** A double click on Search Page item will cause fooDao to be N.P.E. here *****
                Foo foo = fooDao.load(fooForm.getModelObject().getId());
                setResponsePage(new FooPage(foo, PageType.EDIT));
            }
        };
        add(editButton);

        // some stuff here that makes save button invisible in READ_ONLY, and Edit visible only in READ_ONLY
        // delete button is similar (visible only in CREATE)
    }
}

Answers


The dependency fields should not be marked as transient, they should be serialized along the page. The wicket-spring module injects serializable proxies into @SpringBean-annotated fields at component/page creation time, so that they can be safely serialized, without worry about serializing the dependencies themselves.


Need Your Help

Google Places Autocomplete - Pick first result on Enter key?

javascript google-maps google-maps-api-3 google-places-api google-places

I'm using a Google Places Autocomplete and I simply want it to select the top item in the results list when the enter key is pressed in the form field and suggestions exist. I know this has been a...

create scrollable area with controls in dynamically created dialog box by winapi

winapi dialog scrollbar

I create library that allows creating dialog boxes dynamically by DialogBoxIndirectParam. And provides any messages from DlgProc and to controls.