如何用JSF实现动态行列表格的多列垂直单选按钮?
动态行列表格中实现列内单选互斥的JSF方案
核心思路
HTML原生单选按钮的互斥依赖相同的name属性,因此我们可以通过JSF组件绑定同一列的单选按钮到同一个后台属性,让组件自动生成相同的name;或者利用PrimeFaces的自定义布局单选组件来适配动态渲染场景。
方案一:使用标准JSF组件(h:selectOneRadio)
通过ui:repeat动态生成行列,将同一列的单选按钮绑定到后台Map的同一key下,确保组件生成相同的name属性,实现列内互斥。
前台代码
<h:form> <table border="1"> <!-- 表头 --> <thead> <tr> <ui:repeat value="#{dynamicTableBean.columns}" var="column"> <th>#{column.label}</th> </ui:repeat> </tr> </thead> <!-- 表体 --> <tbody> <ui:repeat value="#{dynamicTableBean.rows}" var="row"> <tr> <ui:repeat value="#{dynamicTableBean.columns}" var="column"> <td> <!-- 每个单元格仅一个单选选项,绑定到对应列的选中值 --> <h:selectOneRadio value="#{dynamicTableBean.columnSelectedValues[column.id]}" layout="pageDirection"> <f:selectItem itemValue="#{row.id}" itemLabel="" /> </h:selectOneRadio> </td> </ui:repeat> </tr> </ui:repeat> </tbody> </table> <h:commandButton value="提交选中数据" action="#{dynamicTableBean.handleSubmit}" /> </h:form>
后台Bean代码
@ManagedBean @ViewScoped public class DynamicTableBean implements Serializable { // 动态列数据(含唯一标识id和显示标签label) private List<TableColumn> columns; // 动态行数据(含唯一标识id) private List<TableRow> rows; // 存储每列的选中行ID:key为列id,value为选中的行id private Map<String, String> columnSelectedValues = new HashMap<>(); @PostConstruct public void initData() { // 模拟初始化动态列和行,实际可从数据库/接口获取 columns = Arrays.asList( new TableColumn("col_01", "项目A"), new TableColumn("col_02", "项目B"), new TableColumn("col_03", "项目C") ); rows = Arrays.asList( new TableRow("row_01"), new TableRow("row_02"), new TableRow("row_03") ); } public void handleSubmit() { // 处理提交的选中数据 for (Map.Entry<String, String> entry : columnSelectedValues.entrySet()) { System.out.printf("列[%s] 选中行ID:%s%n", entry.getKey(), entry.getValue()); } } // Getter & Setter public List<TableColumn> getColumns() { return columns; } public List<TableRow> getRows() { return rows; } public Map<String, String> getColumnSelectedValues() { return columnSelectedValues; } public void setColumnSelectedValues(Map<String, String> columnSelectedValues) { this.columnSelectedValues = columnSelectedValues; } } // 列数据辅助类 class TableColumn implements Serializable { private String id; private String label; public TableColumn(String id, String label) { this.id = id; this.label = label; } // Getter & Setter public String getId() { return id; } public String getLabel() { return label; } } // 行数据辅助类 class TableRow implements Serializable { private String id; public TableRow(String id) { this.id = id; } // Getter & Setter public String getId() { return id; } }
方案二:使用PrimeFaces组件解决兼容问题
如果使用PrimeFaces,p:selectOneRadio与ui:repeat的兼容问题可通过**自定义布局(layout="custom")**解决,配合p:radioButton手动渲染单选选项。
前台代码
<h:form> <table border="1"> <thead> <tr> <ui:repeat value="#{dynamicTableBean.columns}" var="column"> <th>#{column.label}</th> </ui:repeat> </tr> </thead> <tbody> <ui:repeat value="#{dynamicTableBean.rows}" var="row"> <tr> <ui:repeat value="#{dynamicTableBean.columns}" var="column"> <td> <p:selectOneRadio value="#{dynamicTableBean.columnSelectedValues[column.id]}" layout="custom"> <f:selectItem itemValue="#{row.id}" /> </p:selectOneRadio> <!-- 关联父级selectOneRadio,渲染单选按钮 --> <p:radioButton for="@parent" itemValue="#{row.id}" /> </td> </ui:repeat> </tr> </ui:repeat> </tbody> </table> <p:commandButton value="提交" action="#{dynamicTableBean.handleSubmit}" ajax="false" /> </h:form>
说明
layout="custom"让p:selectOneRadio不自动渲染选项,由p:radioButton手动控制for="@parent"将单选按钮关联到父级的p:selectOneRadio,确保同一列的组件共享同一个name属性,实现互斥
注意事项
- 后台Map需提前初始化(如
HashMap),避免空指针异常 - 使用
@ViewScoped作用域,确保提交时数据不丢失 - 若行/列数据来自外部数据源,需保证每个行、列的id唯一,避免单选组冲突
内容的提问来源于stack exchange,提问作者DaniEll
相关产品推荐
相关产品推荐

