includeViewParams = true将空模型值转换为查询字符串中的空字符串
给出一个<p:selectOneMenu>
如下.
<f:metadata>
<f:viewParam name="id" value="#{testManagedBean.id}" converter="javax.faces.Long"/>
</f:metadata>
<p:selectOneMenu value="#{localeBean.language}" onchange="changeLanguage();">
<f:selectItem itemValue="en" itemLabel="English" />
<f:selectItem itemValue="hi" itemLabel="Hindi" />
</p:selectOneMenu>
<p:remoteCommand action="#{testManagedBean.submitAction}"
name="changeLanguage"
process="@this"
update="@none"/>
相应的托管bean:
@ManagedBean
@RequestScoped
public final class TestManagedBean {
private Long id; //Getter and setter.
public TestManagedBean() {}
public String submitAction() {
return FacesContext.getCurrentInstance().getViewRoot().getViewId() + "?faces-redirect=true&includeViewParams=true";
}
}
由<f:viewParam>
指示的参数是可选的.例如,使用URL如下访问页面.
The parameter as indicated by <f:viewParam>
is optional. A page, for example is accessed using a URL as follows.
https://localhost:8181/Project-war/private_resources/Test.jsf
由于id
是可选参数,因此在未提供以下内容的情况下,会将URL附加空参数(当语言从<p:selectOneMenu>
更改时).
Since id
is an optional parameter, an empty parameter is attached to the URL (when a language is changed from <p:selectOneMenu>
), in case it is not supplied as follows.
https://localhost:8181/Project-war/private_resources/Test.jsf?id=
这不应该发生.如果未提供空参数,则不应附加空参数,并且URL看起来应该像第一个一样.
This should not happen. An empty parameter should not be appended, if it is not supplied and the URL should look like the first one.
有没有一种方法可以防止在不传递空参数时将其附加到URL?
Is there a way to prevent an empty parameter from being appended to the URL, when it is not passed?
这仅与<f:viewParam>
-javax.faces.Long
指定的转换器相关联.
This is only associated with the converter as specified with <f:viewParam>
- javax.faces.Long
.
如果删除了此转换器,则在不提供任何参数的情况下,不会将参数附加到URL.
If this converter is removed then, parameters are not appended to the URL, in case no parameters are supplied.
尽管完全没有必要像此处演示的那样指定转换器,但我具有如下所示的转换器,可以将通过URL传递的id
作为查询字符串参数转换为JPA实体.
Although specifying a converter as demonstrated here is completely unnecessary, I have converters as shown below to convert an id
passed though the URL as a query-string parameter to a JPA entity.
@ManagedBean
@RequestScoped
public final class ZoneConverter implements Converter {
@EJB
private final SharableBeanLocal sharableService = null;
@Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
try {
long parsedValue = Long.parseLong(value);
if (parsedValue <= 0) {
throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Message Summary", "Message"));
}
ZoneTable entity = sharableService.findZoneById(parsedValue);
if (entity == null) {
throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_WARN, "Message Summary", "Message"));
}
return entity;
} catch (NumberFormatException e) {
throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Message Summary", "Message"), e);
}
}
@Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
return value instanceof ZoneTable ? ((ZoneTable) value).getZoneId().toString() : "";
}
}
现在需要使用<f:viewParam>
明确指定此转换器,如下所示.
This converter is now required to be specified explicitly with <f:viewParam>
as follows.
<f:viewParam name="id"
value="#{testManagedBean.id}"
converter="#{zoneConverter}"
rendered="#{not empty param.id}"/>
关联的受管Bean需要进行如下更改.
And the associated managed bean needs to be changed as follows.
@ManagedBean
@RequestScoped
public final class TestManagedBean {
private ZoneTable id; //Getter and setter.
public TestManagedBean() {}
public String submitAction() {
return FacesContext.getCurrentInstance().getViewRoot().getViewId() + "?faces-redirect=true&includeViewParams=true";
}
}
这可能是 UIViewParameter#getStringValueFromModel()
,其来源供参考复制粘贴如下:
This is likely an oversight in Mojarra's default implementation of UIViewParameter#getStringValueFromModel()
whose source is for reference copypasted below:
384 public String getStringValueFromModel(FacesContext context)
385 throws ConverterException {
386 ValueExpression ve = getValueExpression("value");
387 if (ve == null) {
388 return null;
389 }
390
391 Object currentValue = ve.getValue(context.getELContext());
392
393 // If there is a converter attribute, use it to to ask application
394 // instance for a converter with this identifer.
395 Converter c = getConverter();
396
397 if (c == null) {
398 // if value is null and no converter attribute is specified, then
399 // return null (null has meaning for a view parameters; it means remove it).
400 if (currentValue == null) {
401 return null;
402 }
403 // Do not look for "by-type" converters for Strings
404 if (currentValue instanceof String) {
405 return (String) currentValue;
406 }
407
408 // if converter attribute set, try to acquire a converter
409 // using its class type.
410
411 Class converterType = currentValue.getClass();
412 c = context.getApplication().createConverter(converterType);
413
414 // if there is no default converter available for this identifier,
415 // assume the model type to be String.
416 if (c == null) {
417 return currentValue.toString();
418 }
419 }
420
421 return c.getAsString(context, this, currentValue);
422 }
在为includeViewParams=true
构建查询字符串期间,每个UIViewParameter
(位于<f:viewParam>
之后的UI组件)都会调用此方法.我们在源代码中看到,无论currentValue
是否为null
,它都将调用转换器.换句话说,即使模型值是null
,它仍然会使用它来调用转换器.
This method is called for every UIViewParameter
(the UI component behind <f:viewParam>
) during building the query string for includeViewParams=true
. We see in the source that it calls the converter regardless of whether currentValue
is null
or not. In other words, even if the model value is null
, it still calls the converter with it.
根据
...
... 返回:如果值为 Returns: a zero-length String if value is 因此,转换器实际上应该永远不会在 So, converters are actually supposed to never return 我已经以 issue 3288 的形式向Mojarra的人报告了它.然后,他们应按以下步骤解决此问题: I've reported it to Mojarra guys as issue 3288. They should then fix this problem as follows: 同时,我已经提交了 OmniFaces解决方案.此修复程序扩展了 In the meanwhile, I've committed a solution to OmniFaces. The
更新:他们决定不对其进行修复.无论如何,都有OmniFaces.
Update: they decided to not fix it. In any case, there's OmniFaces. getAsString
getAsString
null
,则为零长度的字符串,否则为转换结果null
, otherwise the result of the conversiongetAsString()
上返回null
.然后,他们返回一个空字符串.对于查询字符串中的视图参数,这是非常不希望的.空字符串值和查询字符串完全不存在之间的区别确实非常重要. null
on getAsString()
. They return an empty string then. In case of view parameters in query string, this is highly undesirable. The difference between an empty string value and a complete absence in query string is really significant. 391 Object currentValue = ve.getValue(context.getELContext());
392
393 if (currentValue == null) {
394 return null;
395 }
<o:viewParam>
.根据今天的 1.8快照可用. <o:viewParam>
has been extended with this fix. It's available as per today's 1.8 snapshot.<f:metadata>
<o:viewParam name="id" value="#{testManagedBean.id}" converter="javax.faces.Long"/>
</f:metadata>