在JSF中制作多个依赖/级联选择组件

10

我正在尝试创建4个相互依赖/级联的选择组件。 在这个问题中,选择组件是一个 <h:selectOneMenu>,但当然也适用于从 UISelectOne/UISelectMany 超类继承的任何其他类型的选择组件,例如 <h:selectManyCheckbox> 或 PrimeFaces 的 <p:selectCheckboxMenu><p:selectManyMenu> 等。

当用户从第一个菜单中选择项目时,第二个菜单将显示相关数据,当用户从第二个菜单中选择项目时,第三个菜单将显示相关数据,以此类推。

用户只会在第一个菜单中看到选项,而其他菜单将为空白。 如果他在第一个菜单上选择一个项目,则第二个菜单将显示数据,但第三个和第四个菜单将保持为空白,以此类推。 用户最终必须从所有4个菜单中选择条目。

<h:selectOneMenu id="first" value="#{nodes.selectState}">
    <f:selectItems value="#{nodes.stateList}"/>
    <f:ajax render="second">
</h:selectOneMenu>
<h:selectOneMenu id="second" value="#{nodes.selectCity}">
    <f:selectItems value="#{nodes.cityList}"/>
    <f:ajax render="third">
</h:selectOneMenu>
<h:selectOneMenu id="third" value="#{nodes.selectRegion}">
    <f:selectItems value="#{nodes.regionList}"/>
    <f:ajax render="fourth">
</h:selectOneMenu>
<h:selectOneMenu id="fourth" value="#{nodes.selectStation}">
    <f:selectItems value="#{nodes.stationList}"/>
</h:selectOneMenu>

节点bean

private String selectState; //+setters, getters
private String selectCity; //+setters, getters
private String selectRegion; //+setters, getters
private String selectStation; //+setters, getters
private List<SelectItem> stateList; //+setters, getters
private List<SelectItem> cityList; //+setters, getters
private List<SelectItem> regionList; //+setters, getters
private List<SelectItem> stationList; //+setters, getters

public getStateList(){
    stateList= new ArrayList<SelectItem>();
    stateList.add(new SelectItem("A"));
}

public getCityList(){
    CityList= new ArrayList<SelectItem>();
    if(selectState.equals("A")){
        CityList.add(new SelectItem("B"));
    }
}

public getRegionList(){
    RegionList= new ArrayList<SelectItem>();
    if(selectCity.equals("B")){
        RegionList.add(new SelectItem("C"));
   }
}

public getStationList(){
    StationList= new ArrayList<SelectItem>();
    if(selectRegion.equals("C")){
        StationList.add(new SelectItem("D"));
    }
}

这只适用于前两个菜单。另外两个菜单得到了null的值。

4个回答

11

将bean放入视图范围中,不要在getter方法中包含任何业务逻辑。

必须将bean放置在视图范围中,以便记住所有先前的选择和新的可用项目,以供后续的提交使用,否则当JSF需要对所选项目进行验证,并与之前选择的可用项目列表进行匹配时,或者当rendered属性依赖于仅在上一次请求中设置的条件时,会出现问题。

getter方法可能不包含任何业务逻辑,因为它们也将在验证阶段等其他情况下被调用。应该使用<f:ajax listener>/<p:ajax listener>基于更改执行业务逻辑。在监听器方法中,您还应显式清除子选择组件的选定值。您可以使用<f:ajax render>/<p:ajax update>来更新子选择组件的内容。

因此:

<h:selectOneMenu id="state" value="#{nodes.selectedState}">
    <f:selectItem itemValue="#{null}" itemLabel="-- select --" />
    <f:selectItems value="#{nodes.availableStates}" />
    <f:ajax listener="#{nodes.changeState}" render="city region station" />
</h:selectOneMenu>
<h:selectOneMenu id="city" value="#{nodes.selectedCity}">
    <f:selectItem itemValue="#{null}" itemLabel="-- select --" />
    <f:selectItems value="#{nodes.availableCities}" />
    <f:ajax listener="#{nodes.changeCity}" render="region station" />
</h:selectOneMenu>
<h:selectOneMenu id="region" value="#{nodes.selectedRegion}">
    <f:selectItem itemValue="#{null}" itemLabel="-- select --" />
    <f:selectItems value="#{nodes.availableRegions}" />
    <f:ajax listener="#{nodes.changeRegion}" render="station" />
</h:selectOneMenu>
<h:selectOneMenu id="station" value="#{nodes.selectedStation}">
    <f:selectItem itemValue="#{null}" itemLabel="-- select --" />
    <f:selectItems value="#{nodes.availableStations}" />
</h:selectOneMenu>

随着

@Named
@ViewScoped
public class Nodes {

    private String selectedState; // getter+setter
    private String selectedCity; // getter+setter
    private String selectedRegion; // getter+setter
    private String selectedStation; // getter+setter
    private List<SelectItem> availableStates; // getter (no setter necessary!)
    private List<SelectItem> availableCities; // getter (no setter necessary!)
    private List<SelectItem> availableRegions; // getter (no setter necessary!)
    private List<SelectItem> availableStations; // getter (no setter necessary!)

    @EJB
    private SomeService someService;

    @PostConstruct
    public void init() {
        availableStates = someService.listStates();
    }

    public void changeState(AjaxBehaviorEvent event) {
        availableCities = someService.listCities(selectedState);
        selectedCity = selectedRegion = selectedStation = null;
        availableRegions = availableStations = null;
    }

    public void changeCity(AjaxBehaviorEvent event) {
        availableRegions = someService.listRegions(selectedCity);
        selectedRegion = selectedStation = null;
        availableStations = null;
    }

    public void changeRegion(AjaxBehaviorEvent event) {
        availableStations = someService.listStations(selectedRegion);
        selectedStation = null;
    }

    // Generate necessary getters+setters here. You should not change them.
}

另请参阅:


-1

试试这个,可能会对你有帮助

通过使用“选择城市”,“选择地区”,“选择站点”来避免空指针异常。

    public getStateList(){
    stateList= new ArrayList<SelectItem>();
    stateList.add(new SelectItem("A"));
    }

    public getCityList(){
    CityList= new ArrayList<SelectItem>();
    if(selectState.equals("A"))
    {
      CityList.add(new SelectItem("B"));
    }
    else
    {
      CityList.add(new SelectItem("--Select City--"));
      selectCity = "--Select City--";
    }

    public getRegionList(){
    RegionList= new ArrayList<SelectItem>();
    if(selectCity.equals("B"))
    {
      RegionList.add(new SelectItem("C"));
    }
    else
    {
      RegionList.add(new SelectItem("--Select Region--"));
      selectRegion = "--Select Region--";
    }
    }

    public getStationList(){
    StationList= new ArrayList<SelectItem>();
    if(selectRegion.equals("C"))
    {
       StationList.add(new SelectItem("D"));
    }
    else
    {
      StationList.add(new SelectItem("Select Station"));
      selectStation = "--Select Station--";
    }
    }

2
如果您尝试回答,应该提供文本解释,以便读者和 OP 能够理解代码中的缺陷,并且确切地知道应该采取什么措施来避免错误。 - skuntsel
问题不在Java文件中,而是在Ajax渲染中。第一个菜单呈现了第二个菜单,但第二个菜单无法呈现第三个菜单,因此selectCity值无法到达Java代码。 - Shadooo Medo

-1

你的代码中有一个拼写错误。对于第三个菜单,你给出的id名称是“first”,而不是“third”。可能是因为这个问题。


-5
你遇到这个问题是因为你有两个 id="first"。 修复它,它应该可以工作。

1
这只是在准备问题时的一个打字错误/复制粘贴错误。如果真的使用了相同的ID,那么OP在打开页面时就已经遇到了“IllegalArgumentException: duplicate component ID”,而不是像“仅在前两个菜单中工作,其他两个菜单获取空值”所描述的问题。 - BalusC

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接