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