koszek
koszek

Reputation: 55

Wicket - Form Component List with add/remove functionality

In my web application I have many forms with lists of components, users can dynamically add/remove those components. For example when entering information about himself a user might add several text fields containing his children's names. There's a remove link for every component and also an add link to add a new component. The markup looks like this:

<div>
    <div wicket:id="rows">
        <input type="text" wicket:id="name"/>
        <a wicket:id="remove">Remove</a>
    </div>
    <a wicket:id="add">Add</a>
</div>

where rows is a wicket repeater.

The input components vary from list to list (it may be a dropdown or something else, there may be different validators added to it, and so on) but the links stay the same. Their markup and logic do not vary. Because I have many such lists in my forms I have makup and code duplication. What I would like to have is to keep the markup of my input components in the form while getting rid of those links, something like this:

<div wicket:id="dynamicList">
    <input type="text" wicket:id="name"/>
</div>

which would be rendered as a list of text fields with add/remove links.

I know I must have the markup for the links somewhere, like autogenerate them or put them in a panel or something, I just don't want to duplicate them each time.

UPD: Here's the markup for the suggested solution (that didn't work) using Border component: ListBorder.html:

<wicket:border>
    <div wicket:id="rows">
        <wicket:body/>
        <a wicket:id="remove">Remove</a>
    </div>
    <a wicket:id="add">Add</a>
</wicket:border>

MyForm.html:

<form wicket:id="form">
    ...
    <div wicket:id="dynamicList">
        <input type="text" wicket:id="name"/>
    </div>
    ...
</form>

Upvotes: 0

Views: 1145

Answers (1)

Domas Poliakas
Domas Poliakas

Reputation: 876

I think there's a few distinct facets in the problem you're describing. Firstly, you want to wrap an input such that a remove link would be added to it. You want to then repeat this wrapped input. Finally, you want to append an add link at the end of the repeated input. And finally you want this whole thing wrapped in a single component.

Firstly, adding a remove link to an input. This is where you can use a Border. As you can probably infer, it would look something like

<wicket:border>
    <wicket:body/>
    <a wicket:id="remove">Remove</a>
</wicket:border>

You then repeat this whole border to achieve multiple inputs with remove links automatically.

To then achieve the exact behavior you want as far as the rest of the question is concerned, you can do it by overriding the Component#onComponentTag(). You can manually the HTML for the add link like

@Override
protected void onRender() {
    super.onRender();
    Response response = getResponse();
    String url = generateAddUrl();
    response.write("<a href=\"" + url + "\">Add</a>");        
}

The way you generate the URL is a tricky one. I'm not going to cover it (because I think the exact behavior that you're asking is not feasible - I'll explain why in a sec), but you can use the source code for Link#getUrl() as a starting point.

Alternatively, you could write your own Component implementation which would scan the body of its markup, and then generate what you desire based on that. Obviously this is not a quick solution, but if the use-case of what you describe is large enough, it might be better in the long term.

But in the end I believe the better solution would be having a Panel for every type of input (i.e. input, select, etc.) with markup structured the way you want, and providing the necessary markup alterations via wicket behaviors like AttributeAppender and such. This would of course mean that some of the html would end up on the java side of things, which is not ideal, but this solution would work well and can be set up fast.

Upvotes: 1

Related Questions