I'm building an app that will have *lots* of panels that all follow a very similar pattern:
They have a form and a static view whose visibility gets toggled when when you click edit/cancel|save|delete They display a warning if their model is empty They pop up a warning when you delete them They allow you to edit their model I've written a few and realized that I am cutting and pasting way too much code; so, I want to build an abstract superclass, but am having trouble figuring out how. Hopefully someone here may be able to help. I need my subclasses to add markup to both the static and form components, which I believe rules out markup inheritance because I would have to use <wicket:child/> twice and can't imagine wicket being able to figure out what I meant. The other option I see is to create a panel for both the static content and the form, and to extend the superclass to specify which panels to include for each, which seems workable but ugly... Am I missing something, is there a better way? The super class I want to write would look something like this, I just can't figure out how to associate the markup: public abstract class AttributePanel extends Panel { WebMarkupContainer staticContainer = new WebMarkupContainer("static"); WebMarkupContainer dynamicContainer = new WebMarkupContainer("dynamic"); AjaxLink editLink; Form form; Label unprovisioned = new Label("unprovisioned", "No provisioning data has been entered for this service, perhaps you should add it now...");; Attributable attribute; //the model ComponentFeedbackPanel feedback = new ComponentFeedbackPanel("feedback", this); String ConfirmationMsg = "You sure?"; public AttributePanel(final String id, final ModelService service) { super(id, service); // add static stuff staticContainer.setOutputMarkupId(true) .setOutputMarkupPlaceholderTag(true); add(staticContainer); staticContainer.add(unprovisioned.setVisible(false)); if (attribute.getRefItemID()==null) { unprovisioned.setVisible(true); } populateStaticContainer(); //add some components specific to the subclass //add dynamic stuff dynamicContainer.setOutputMarkupId(true) .setOutputMarkupPlaceholderTag(true); add(dynamicContainer); form = new Form("form"); form.add(new AjaxButton("submitButton", form) { @Override protected void onSubmit(AjaxRequestTarget target, Form form) { save(); ServicePanel parent = (ServicePanel) findParent(ServicePanel.class); parent.addOrReplace(this); target.addComponent(parent); } @Override protected void onError(AjaxRequestTarget target, Form form) { target.addComponent(feedback); } }); feedback.setOutputMarkupId(true); form.add(feedback); dynamicContainer.add(form); populateDynamicContainer(); //add some components specific to the subclass // Link to show form editLink = new AjaxLink("edit") { @Override public void onClick(AjaxRequestTarget target) { showForm(target); } }; staticContainer.add(editLink); // Link to return to static view without saving any changes form.add(new AjaxLink("cancel") { public void onClick(AjaxRequestTarget target) { hideForm(target); } }); //link to delete attribute form.add(new Link("deprovision") { @Override public void onClick() { deprovision(); ServicePanel parent = (ServicePanel) findParent(ServicePanel.class); parent.addOrReplace(this); } }.add(new SimpleAttributeModifier("onclick", "return confirm('" +getConfirmationMsg()+"'); "))); } private void showForm(AjaxRequestTarget target) { staticContainer.setVisible(true); dynamicContainer.setVisible(false); target.addComponent(staticContainer); target.addComponent(dynamicContainer); } private void hideForm(AjaxRequestTarget target) { staticContainer.setVisible(true); dynamicContainer.setVisible(false); target.addComponent(staticContainer); target.addComponent(dynamicContainer); } private String getConfirmationMsg(){ return ConfirmationMsg; } abstract void populateStaticContainer(); abstract void populateDynamicContainer(); abstract boolean save(); abstract boolean deprovision(); } Thanks! Loren