blob: 0c764bdc37b2d3cc9f004fc81dcf6dab327c1aa6 [file] [log] [blame]
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package org.apache.tapestry5.corelib.components;
import org.apache.tapestry5.*;
import org.apache.tapestry5.annotations.Parameter;
import org.apache.tapestry5.annotations.Property;
import org.apache.tapestry5.corelib.base.AbstractField;
import org.apache.tapestry5.dom.Element;
import org.apache.tapestry5.ioc.annotations.Inject;
import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
import org.apache.tapestry5.services.Request;
import java.util.Collections;
import java.util.List;
import java.util.Set;
/**
* A list of checkboxes, allowing selection of multiple items in a list.
*
* For an alternative component that can be used for similar purposes, see
* {@link Palette}.
*
* @tapestrydoc
* @see Form
* @see Palette
* @since 5.3
*/
public class Checklist extends AbstractField
{
/**
* Model used to define the values and labels used when rendering the
* checklist.
*/
@Parameter(required = true)
private SelectModel model;
/**
* The list of selected values from the
* {@link org.apache.tapestry5.SelectModel}. This will be updated when the
* form is submitted. If the value for the parameter is null, a new list
* will be created, otherwise the existing list will be cleared. If unbound,
* defaults to a property of the container matching this component's id.
*/
@Parameter(required = true, autoconnect = true)
private List<Object> selected;
/**
* A ValueEncoder used to convert server-side objects (provided from the
* "source" parameter) into unique client-side strings (typically IDs) and
* back. Note: this component does NOT support ValueEncoders configured to
* be provided automatically by Tapestry.
*/
@Parameter(required = true, allowNull = false)
private ValueEncoder<Object> encoder;
/**
* The object that will perform input validation. The validate binding prefix is generally used to provide
* this object in a declarative fashion.
*/
@Parameter(defaultPrefix = BindingConstants.VALIDATE)
@SuppressWarnings("unchecked")
private FieldValidator<Object> validate;
@Inject
private Request request;
@Inject
private FieldValidationSupport fieldValidationSupport;
@Property
private List<Renderable> availableOptions;
private final class RenderCheckbox implements Renderable
{
private final OptionModel model;
private RenderCheckbox(final OptionModel model)
{
this.model = model;
}
public void render(MarkupWriter writer)
{
final String clientValue = encoder.toClient(model.getValue());
writer.element("label");
final Element checkbox = writer.element("input",
"type", "checkbox",
"name", getControlName(),
"value", clientValue);
if (getSelected().contains(model.getValue()))
{
checkbox.attribute("checked", "checked");
}
if (isDisabled()) {
writer.attributes("disabled", "disabled");
}
writer.write(model.getLabel());
writer.end();
writer.end();
}
}
void setupRender()
{
availableOptions = CollectionFactory.newList();
final SelectModelVisitor visitor = new SelectModelVisitor()
{
public void beginOptionGroup(final OptionGroupModel groupModel)
{
}
public void option(final OptionModel optionModel)
{
availableOptions.add(new RenderCheckbox(optionModel));
}
public void endOptionGroup(final OptionGroupModel groupModel)
{
}
};
model.visit(visitor);
}
@Override
protected void processSubmission(final String controlName)
{
final String[] parameters = request.getParameters(controlName);
List<Object> selected = this.selected;
if (selected == null)
{
selected = CollectionFactory.newList();
} else
{
selected.clear();
}
if (parameters != null)
{
for (final String value : parameters)
{
final Object objectValue = encoder.toValue(value);
selected.add(objectValue);
}
}
putPropertyNameIntoBeanValidationContext("selected");
try
{
fieldValidationSupport.validate(selected, this.resources, this.validate);
this.selected = selected;
} catch (final ValidationException e)
{
validationTracker.recordError(this, e.getMessage());
}
removePropertyNameFromBeanValidationContext();
}
Set<Object> getSelected()
{
if (selected == null)
{
return Collections.emptySet();
}
return CollectionFactory.newSet(selected);
}
/**
* Computes a default value for the "validate" parameter using
* {@link org.apache.tapestry5.services.FieldValidatorDefaultSource}.
*/
Binding defaultValidate()
{
return this.defaultProvider.defaultValidatorBinding("selected", resources);
}
@Override
public boolean isRequired()
{
return validate.isRequired();
}
void beginRender(MarkupWriter writer) {
writer.element("div", "id", getClientId());
}
void afterRender(MarkupWriter writer) {
writer.end();
}
}