tags:

views:

280

answers:

1

I try to create an page of the following structure:

MainPage {
    DateSelector {}
    TabGroup {
        Tab0 {...}
        Tab1 {...}
        Tab2 {...}
    }
}

DateSelector is a component (extending Panel) that enables the user to select a timespan (startDate, endDate). TabGroup extending "AjaxTabbedPanel". The tabs shown in the TabGroup should use the selected timespan to display content.

So if the user selects a timespan in the selector the content in the selected tab should update.

For this I implement a method the user of the DateSelector could override to update its contents.

I have the feeling that my approch is against the framework. Could I utilise a model in some way to enhance the implementation? What is 'wrong' with this? Can the tabs share the timespan with the DateSelector, such that they update if teh timespan change? Without the callback?

The DateSelector is implemented like this:

abstract public class TimespanSelectionPanel extends Panel {

    public TimespanSelectionPanel(String id, Timespan timespan) {
        super(id, new CompoundPropertyModel<Timespan>(timespan));

        final DateTextField fromTextField = DateTextField.forDatePattern("from", "dd.MM.yyyy");
        fromTextField.setOutputMarkupId(true);
        fromTextField.add(new DatePicker());
        final DateTextField toTextField = DateTextField.forDatePattern("to", "dd.MM.yyyy");
        toTextField.setOutputMarkupId(true);
        toTextField.add(new DatePicker());

        fromTextField.add(new AjaxFormComponentUpdatingBehavior("onchange") {
            @Override
            protected void onUpdate(AjaxRequestTarget target) {
            }
        });
        toTextField.add(new AjaxFormComponentUpdatingBehavior("onchange") {
            @Override
            protected void onUpdate(AjaxRequestTarget target) {
            }
        });

        add(new AjaxLink<String>("today") {
            @Override
            public void onClick(AjaxRequestTarget target) {
                updateComponent(fromTextField, toTextField, target, Timespan.today());
            }
        });
        add(new AjaxLink<String>("yesterday") {
            @Override
            public void onClick(AjaxRequestTarget target) {
                updateComponent(fromTextField, toTextField, target, Timespan.yesterday());
            }
        });
        add(new AjaxLink<String>("lastMonth") {
            @Override
            public void onClick(AjaxRequestTarget target) {
                updateComponent(fromTextField, toTextField, target, Timespan.lastMonth());
            }
        });
        add(new AjaxLink<String>("actualMonth") {
            @Override
            public void onClick(AjaxRequestTarget target) {
                Timespan timespan = adjustToTextField(fromTextField, toTextField, target, Timespan.actualMonth());
                updateComponent(fromTextField, toTextField, target, timespan);
            }
        });
        add(new AjaxLink<String>("fromTo") {
            @Override
            public void onClick(AjaxRequestTarget target) {
                Timespan newTimespan = adjustToTextField(fromTextField, toTextField, target, fromTextField
                        .getModelObject(), toTextField.getModelObject());
                update(target, newTimespan);
            }
        });
        add(fromTextField);
        add(toTextField);
    }

    private Timespan adjustToTextField(DateTextField fromTextField, DateTextField toTextField,
            AjaxRequestTarget target, Timespan timespan) {
        return adjustToTextField(fromTextField, toTextField, target, timespan.getFrom(), timespan.getTo());
    }

    private Timespan adjustToTextField(final DateTextField fromTextField, final DateTextField toTextField,
            AjaxRequestTarget target, Date from, Date to) {
        Date today = DateUtil.today();
        if (to.after(today)) {
            to = today;
            toTextField.setModelObject(today);
            target.addComponent(toTextField);
        }
        if (from.after(to)) {
            from = to;
            fromTextField.setModelObject(from);
            target.addComponent(fromTextField);
        }
        return Timespan.fromTo(from, to);
    }

    private void updateComponent(final DateTextField fromTextField, final DateTextField toTextField,
            AjaxRequestTarget target, Timespan newTimespan) {
        fromTextField.setModelObject(newTimespan.getFrom());
        toTextField.setModelObject(newTimespan.getTo());
        target.addComponent(fromTextField);
        target.addComponent(toTextField);
        update(target, newTimespan);
    }

    abstract protected void update(AjaxRequestTarget target, Timespan timespan);

}
A: 

If the timespan is always in synch between the selector and tab panels, then it makes sense that it is a shared model.

This means your tab panels need access to a either a model that supplies a TimeSpan, or something like an ITimeSpanProvider implementor (i.e. the timespan selector component). The former is obviously the better way of doing things in Wicket.

So, your panel constructors should take a TimeSpan model, which is then used by whatever components require it. This means that you don't need to push model updates from the selector to the tab panels.

Depending on the function of your tab panels, the TimeSpan model may not make sense as the "main" model, but that's fine - it's just another model. If the timespan model isn't set as a model for another component within your tab panels, you should be nice and detach() the model when appropriate, etc. You can abstract this all out into a TimeSpanAwarePanel if it fits what you're doing.

ireddick