HashMapチェックボックスのリストMap<String, Boolean>を成功にバインドするメソッドを使用しました。チェックボックスの数を動的に設定できるため、これは便利です。
それを の可変長リストに拡張しようとしていますselectManyMenu。それらはselectManyであるため、にバインドできるようにしたいと思いMap<String, List<MyObject>>ます。selectManyMenuシングルを a にバインドできる単一の例がありList<MyObject>、すべて正常に動作しますが、selectManyMenus の動的な数を a 内に配置ui:repeatしてマップにバインドしようとすると、奇妙な結果になります。デバッガーと呼び出しによって検証されるように、値はマップに正しく格納されますtoString()が、ランタイムは、マップの値が型であり、型Objectでないと判断List<MyObject>し、マップのキーにアクセスしようとすると ClassCastExceptions をスローします。
JSFがバインディングのターゲットのランタイムタイプを決定する方法と関係があると思います.aの値にバインディングしているMapため、の値タイプパラメータからタイプを取得することはわかりません地図。おそらくMojarraにパッチを当てる以外に、これに対する回避策はありますか?
一般に、動的な数の selectManyMenus を含むページを作成するにはどうすればよいですか? もちろん、Primefaces の<p:solveThisProblemForMe>コンポーネントを使用せずに。(真剣に言うと、Primefaces は、私の制御の及ばない要因のため、ここではオプションではありません。)
List<T> の UISelectManyという質問により、java.lang.ClassCastException: java.lang.String を T にキャストすることはできません。
JSF:
<ui:define name="content">
<h:form>
<ui:repeat value="#{testBean.itemCategories}" var="category">
<h:selectManyMenu value="#{testBean.selectedItemMap[category]}">
<f:selectItems value="#{testBean.availableItems}" var="item" itemValue="#{item}" itemLabel="#{item.name}"></f:selectItems>
<f:converter binding="#{itemConverter}"></f:converter>
<f:validator validatorId="test.itemValidator"></f:validator>
</h:selectManyMenu>
</ui:repeat>
<h:commandButton value="Submit">
<f:ajax listener="#{testBean.submitSelections}" execute="@form"></f:ajax>
</h:commandButton>
</h:form>
</ui:define>
コンバータ:
@Named
public class ItemConverter implements Converter {
@Inject
ItemStore itemStore;
@Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
return itemStore.getById(value);
}
@Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
return Optional.of(value)
.filter(v -> Item.class.isInstance(v))
.map(v -> ((Item) v).getId())
.orElse(null);
}
}
バッキング Bean:
@Data
@Slf4j
@Named
@ViewScoped
public class TestBean implements Serializable {
private static final long serialVersionUID = 1L;
@Inject
ItemStore itemStore;
List<Item> availableItems;
List<String> itemCategories;
Map<String, List<Item>> selectedItemMap = new HashMap<>();
public void initialize() {
log.debug("Initialized TestBean");
availableItems = itemStore.getAllItems();
itemCategories = new ArrayList<>();
itemCategories.add("First Category");
itemCategories.add("Second Category");
itemCategories.add("Third Category");
}
public void submitSelections(AjaxBehaviorEvent event) {
log.debug("Submitted Selections");
selectedItemMap.entrySet().forEach(entry -> {
String key = entry.getKey();
List<Item> items = entry.getValue();
log.debug("Key: {}", key);
items.forEach(item -> {
log.debug(" Value: {}", item);
});
});
}
}
ItemStore には、ID フィールドによってアイテムにアクセスするための HashMap メソッドとデリゲート メソッドが含まれているだけです。
アイテム:
@Data
@Builder
public class Item {
private String id;
private String name;
private String value;
}
ItemListValidator:
@FacesValidator("test.itemValidator")
public class ItemListValidator implements Validator {
@Override
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
if (List.class.isInstance(value)) {
if (((List) value).size() < 1) {
throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_FATAL, "You must select at least 1 Admin Area", "You must select at least 1 Admin Area"));
}
}
}
}
エラー:
java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to java.util.List
Stacktrace は切り取られましたが、次の行で発生します。
List<Item> items = entry.getValue();
ここで何が欠けていますか?