Dynamic support in wicket

2019-04-08 16:51发布

问题:

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.

回答1:

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>


回答2:

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.



回答3:

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.