jsf - includeViewParams=true converts null model value to empty string in query string -
given <p:selectonemenu>
follows.
<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"/>
the corresponding managed bean :
@managedbean @requestscoped public final class testmanagedbean { private long id; //getter , setter. public testmanagedbean() {} public string submitaction() { return facescontext.getcurrentinstance().getviewroot().getviewid() + "?faces-redirect=true&includeviewparams=true"; } }
the parameter indicated <f:viewparam>
optional. page, example accessed using url follows.
https://localhost:8181/project-war/private_resources/test.jsf
since id
optional parameter, empty parameter attached url (when language changed <p:selectonemenu>
), in case not supplied follows.
https://localhost:8181/project-war/private_resources/test.jsf?id=
this should not happen. empty parameter should not appended, if not supplied , url should first one.
is there way prevent empty parameter being appended url, when not passed?
this associated converter specified <f:viewparam>
- javax.faces.long
.
if converter removed then, parameters not appended url, in case no parameters supplied.
although specifying converter demonstrated here unnecessary, have converters shown below convert id
passed though url query-string parameter 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() : ""; } }
this converter required specified explicitly <f:viewparam>
follows.
<f:viewparam name="id" value="#{testmanagedbean.id}" converter="#{zoneconverter}" rendered="#{not empty param.id}"/>
and associated managed bean needs changed follows.
@managedbean @requestscoped public final class testmanagedbean { private zonetable id; //getter , setter. public testmanagedbean() {} public string submitaction() { return facescontext.getcurrentinstance().getviewroot().getviewid() + "?faces-redirect=true&includeviewparams=true"; } }
this oversight in mojarra's default implementation of uiviewparameter#getstringvaluefrommodel()
source 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 converter attribute, use to ask application 394 // instance converter identifer. 395 converter c = getconverter(); 396 397 if (c == null) { 398 // if value null , no converter attribute specified, 399 // return null (null has meaning view parameters; means remove it). 400 if (currentvalue == null) { 401 return null; 402 } 403 // not "by-type" converters strings 404 if (currentvalue instanceof string) { 405 return (string) currentvalue; 406 } 407 408 // if converter attribute set, try acquire converter 409 // using class type. 410 411 class convertertype = currentvalue.getclass(); 412 c = context.getapplication().createconverter(convertertype); 413 414 // if there no default converter available identifier, 415 // assume model type string. 416 if (c == null) { 417 return currentvalue.tostring(); 418 } 419 } 420 421 return c.getasstring(context, this, currentvalue); 422 }
this method called every uiviewparameter
(the ui component behind <f:viewparam>
) during building query string includeviewparams=true
. see in source calls converter regardless of whether currentvalue
null
or not. in other words, if model value null
, still calls converter it.
as per javadoc of converter#getasstring()
converters specification required return zero-length string if value null
:
getasstring
...
returns: zero-length string if value
null
, otherwise result of conversion
so, converters supposed never return null
on getasstring()
. return empty string then. in case of view parameters in query string, highly undesirable. difference between empty string value , complete absence in query string significant.
i've reported mojarra guys issue 3288. should fix problem follows:
391 object currentvalue = ve.getvalue(context.getelcontext()); 392 393 if (currentvalue == null) { 394 return null; 395 }
in meanwhile, i've committed solution omnifaces. <o:viewparam>
has been extended fix. it's available per today's 1.8 snapshot.
<f:metadata> <o:viewparam name="id" value="#{testmanagedbean.id}" converter="javax.faces.long"/> </f:metadata>
update: decided not fix it. in case, there's omnifaces.
Comments
Post a Comment