Mapとのパラメータバインディング
入力コンポーネントが動的に増減するフォームを作りたい場合があります。リスト状の項目を、1つの画面内で一括編集したいときなどです。
こういうときはリクエストパラメータ名が動的に変化するので、パラメータをMapにバインディングできれば便利だと思い、Cubbyの処理をカスタマイズしてみました。
FormWrapperFactoryのカスタマイズ(Map → HTMLフォーム)
アクション内でMap型のフィールドにセットした初期値を、Cubbyのフォーム用カスタムタグ(
org.seasar.cubby.controller.impl.FormWrapperFactoryImplを以下のように一部書き換えて、cubby.diconに登録します。
public class MappableFormWrapperFactory implements FormWrapperFactory { ... private class FormWrapperImpl implements FormWrapper { public String[] getValues(String name) { ... // 変更ここから Object value; if (form instanceof Map) { value = ((Map<?, ?>) form).get(name); } // 変更ここまで // 以下はBeanDescを使った従来通りの処理 else { BeanDesc beanDesc = BeanDescFactory .getBeanDesc(form.getClass()); if (!beanDesc.hasPropertyDesc(name)) { return null; } PropertyDesc propertyDesc = beanDesc.getPropertyDesc(name); value = propertyDesc.getValue(form); } ... } } ...
RequestParameterBinderのカスタマイズ(HTMLフォーム → Map)
アクションのsubmit時、@Formアノテーションでバインディング先としてMap型のフィールドが指定された場合には、Mapにパラメータ名と値のペアを格納するようにします。値はとりあえず、無条件でStringに変換しています。
このクラスを、org.seasar.cubby.controller.impl.RequestParameterBinderImplの代わりにcubby.diconに登録します。
public class MappableRequestParameterBinder implements RequestParameterBinder { private RequestParameterBinderImpl defaultBinder; private ConverterFactory converterFactory; private ConversionHelper conversionHelper; public MappableRequestParameterBinder() { defaultBinder = new RequestParameterBinderImpl(); } public void setConverterFactory(ConverterFactory converterFactory) { defaultBinder.setConverterFactory(converterFactory); this.converterFactory = converterFactory; } public void setConversionHelper(ConversionHelper conversionHelper) { defaultBinder.setConversionHelper(conversionHelper); this.conversionHelper = conversionHelper; } public void bind(Map<String, Object[]> parameterMap, Object dest, Class<? extends Action> actionClass, Method method) { if (!(dest instanceof Map)) { defaultBinder.bind(parameterMap, dest, actionClass, method); return; } Map<Object, Object> map = (Map<Object, Object>) dest; for (Entry<String, Object[]> entry : parameterMap.entrySet()) { String sourceName = entry.getKey(); map.put(sourceName, convert(entry.getValue())); } } private Object convert(Object[] values) { Converter converter = converterFactory.getConverter(values[0] .getClass(), String.class); if (converter != null) { return converter.convertToObject(values[0], String.class, conversionHelper); } else { return values[0]; } } }
submitされたパラメータが一律でStringになってしまったり、いまいちな感じもしますが、これで多少、動的なフォームが作りやすくなります。