Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamic <optgroup> support in wicket

I'm looking to render a <select> tag in my page using wicket, but group the options with <optgroup>, this was discussed on Separator in a Wicket DropDownChoice, but in the solutions there the <optgroup> assume that the <optgroup> tags are static, I'm wanting to pull both the options and the groups from a database.

like image 206
Matthew Buckett Avatar asked Dec 15 '11 14:12

Matthew Buckett


3 Answers

Use two nested repeaters to iterate over your groups and options:

<select wicket:id="select">
    <optgroup wicket:id="group">
        <option wicket:id="option"></option>
    </optgroup>
</select>
like image 190
svenmeier Avatar answered Oct 05 '22 23:10

svenmeier


I have had basically the same problem. After a few days looking for a short solution, I believe what works best, for maximum flexibility, is using repeaters, containers and AttributeModifier, something like:

<select wicket:id="select">
    <wicket:container wicket:id="repeatingView">
        <optgroup wicket:id="optGroup">
          <wicket:container wicket:id="selectOptions">
            <option wicket:id="option"></option>
          </wicket:container>
        </optgroup>
    </wicket:container>
</select>

In Java code, "select" is a Select; "repeatingView" is a RepeatingView. Nested inside the RepeatingView there's a WebMarkupContainer named by .newChildId(). Nested inside is another WebMarkupContainer that represents "optGroup". Inside this second WMC are an AttributeModifier that adds a dynamic label to the optgroup, and a SelectOptions that processes "selectOptions" and "option". Something like:

Select select = new Select("select");
add(select);

RepeatingView rv = new RepeatingView("repeatingView");
select.add(rv);

for(String groupName : groupNames){

    WebMarkupContainer overOptGroup = new WebMarkupContainer(rv.newChildId());
    rv.add(overGroup);

    WebMarkupContainer optGroup = new WebMarkupContainer("optGroup");
    overOptGroup.add(optGroup);
    optGroup.add(
        new AttributeModifier("label",true,new Model<String>(groupName))
    );
    optGroup.add(
        new SelectOptions<MyBean>(
            "selectOptions",listOfBeanOptionsForThisGroup,new MyBeanRenderer()
        )
    );
}

(this is supposing Strings are passed directly as group names and that the options refer to beans of type MyBean, listed in the variable listOfBeanOptionsForThisGroup)

I suppose it shouldn't be hard to refactor this solution into something that uses much less nesting, if anyone's got suggestions, I'll edit them into the answer and credit them. Using ListView instead of RepeatingView should also reduce code size.

like image 27
userBigNum Avatar answered Oct 06 '22 00:10

userBigNum


Ok, so at the moment my solution is to have something like this:

   interface Thing {
       String getCategory();
   }

and then:

            List<Thing> thingList = service.getThings();
    DropDownChoice<Thing> dropDownChoice = new DropDownChoice<Thing>("select",
            thingList) {
        private static final long serialVersionUID = 1L;
        private Thing last;

        private boolean isLast(int index) {
            return index - 1 == getChoices().size();
        }

        private boolean isFirst(int index) {
            return index == 0;
        }

        private boolean isNewGroup(Thing current) {
            return last == null
                    || !current.getCategory().equals(last.getCategory());
        }

        private String getGroupLabel(Thing current) {
            return current.getCategory();
        }

        @Override
        protected void appendOptionHtml(AppendingStringBuffer buffer,
                Thing choice, int index, String selected) {
            if (isNewGroup(choice)) {
                if (!isFirst(index)) {
                    buffer.append("</optgroup>");
                }
                buffer.append("<optgroup label='");
                buffer.append(Strings.escapeMarkup(getGroupLabel(choice)));
                buffer.append("'>");
            }
            super.appendOptionHtml(buffer, choice, index, selected);
            if (isLast(index)) {
                buffer.append("</optgroup>");
            }
            last = choice;

        }
    };

This requires that thingList is already sorted based on the category.

like image 35
Matthew Buckett Avatar answered Oct 05 '22 23:10

Matthew Buckett