在请求范围中使用依赖选择列表

时间:2010-09-01 09:32:44

标签: java jsf

我们不断向用户提出的问题是具有相关值的选择(下拉列表或多选)。例如,用户选择一个国家/地区,然后系统会使用该国家/地区的城市填充城市下拉列表。

我在会话(或会话范围)中经常做这项工作但现在,对于一个真正的轻量级场景,我希望它在请求范围内工作。

这里有一些显示问题的虚拟代码(通常我们会使用A4J填充下拉列表而不进行完全刷新,但问题也可以用普通的jsf来证明):

JSF:

<h:form>
    <p>
        <h:selectOneMenu value="#{bean.selectedSourceValue}">
            <f:selectItems value="#{bean.sourceValues}" />
        </h:selectOneMenu>
    </p>
    <p>
        <h:selectOneMenu value="#{bean.selectedDependentValue}">
            <f:selectItems value="#{bean.dependentValues}" />
        </h:selectOneMenu>
    </p>
    <p>
        <h:commandButton value="submit" />
    </p>
</h:form>

支持bean:

public class Bean {

    private Integer selectedSourceValue;
    private Integer selectedDependentValue;

    /**
     * @return values for the first selection.  Numbers from 1 to 10.
     */
    public List<SelectItem> getSourceValues(){
        List<SelectItem> r = new ArrayList<SelectItem>();
        for(int i=1; i<=10; i++){
            r.add(new SelectItem(i));
        }
        return r;
    }

    /**
     * @return values for the second selection.  First ten powers of the selected first value.
     */
    public List<SelectItem> getDependentValues(){
        if (selectedSourceValue==null) return Collections.emptyList();
        List<SelectItem> r = new ArrayList<SelectItem>();
        for(int i=1; i<=10; i++){
            r.add(new SelectItem((int)Math.pow(selectedSourceValue, i)));
        }
        return r;
    }

        // ... snipped some basic getter and setters 
}

看起来很简单。问题在于进行第二次选择时。提交第二个下拉列表时,将验证组合。但是在验证阶段,请求范围的bean尚未填充,因此getDependentValues()返回null。这会导致jsf抛出NoSuchElementException(使用Sun RI)。

有关如何解决这个问题的想法,甚至是否可能?

5 个答案:

答案 0 :(得分:1)

是的,您希望根据第二/第三阶段中第一个下拉列表的选定选项预填充第二个下拉列表,远在JSF完成更新模型值阶段之前,因此selectedSourceValue仍然是{{ 1}}。基本上有两种方法可以解决这个问题:

  1. 从请求参数映射中获取提交的源值作为请求参数。

    null

    然而这是令人讨厌的。

  2. 将下拉组件绑定到selectedSourceValue = (Integer) externalContext.getRequestParameterMap().get("clientId"); 属性,并使用其UIInput方法获取提交的值。

    getSubmittedValue()

    <h:selectOneMenu binding="#{bean.sourceMenu}">
    

    然后在private UIInput sourceMenu; // +getter +setter

    getDependentValues()

    更多的工作,但更抽象。

答案 1 :(得分:0)

我认为,你应该寻找基于ajax的组件(Icefaces允许这个技巧,可能是richfaces,primefaces,jsf2(f:ajax)等)。或者在保存按钮actionListener中使用验证(事实证明,它更灵活,至少在我的项目中+你总是可以删除ajax以避免在基于ajax的组件使用partialSubmit = true的情况下向服务器发送大量请求)。

答案 2 :(得分:0)

如果您使用的是JSF2,请尝试:

<h:form>
  <p>
    <h:selectOneMenu id="selectionSource" value="#{bean.selectedSourceValue}">
        <f:selectItems value="#{bean.sourceValues}" />
        <f:ajax execute="selectionSource" render="dependentSelection"/>
    </h:selectOneMenu>
  </p>
  <p>
    <h:selectOneMenu id="dependentSelection" value="#{bean.selectedDependentValue}">
        <f:selectItems value="#{bean.dependentValues}" />
    </h:selectOneMenu>
  </p>
  <p>
    <h:commandButton value="submit" />
  </p>
</h:form>

此处的解决方案:http://pawelstawicki.blogspot.com/2010/03/simple-ajax-with-jsf-20.html

答案 3 :(得分:0)

我最后通过继承UISelectMany来解决它,而不是检查提交的值是否是列表的一部分。这是有效的,在我们的场景中,没有伤害。

答案 4 :(得分:0)

如果你写

@Scope("request")

尝试

@RequestScoped .