Why does the first AJAX call reset my view parameter? - ajax

I cannot find out why the first ajax call causes the setter of my view parameter to be called again while every subsequent call does not call the setter again.
I have the following simple view bean:
package test;
import java.io.Serializable;
import javax.faces.view.ViewScoped;
import javax.inject.Named;
#Named
#ViewScoped
public class TestController implements Serializable {
private static final long serialVersionUID = 1L;
String param;
public String getParam() {
return param;
}
public void setParam(String param) {
System.out.println("param set to " + param);
this.param = param;
}
}
I also have a very basic .xhtml page which only contains a single button:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<h:head></h:head>
<f:metadata>
<f:viewParam id="param" name="param" value="#{testController.param}"/>
</f:metadata>
<h:form id="form">
<h:commandButton id="button" value="Test">
<f:ajax execute="#this"></f:ajax>
</h:commandButton>
</h:form>
</html>
Now when testing this page I call https://localhost:8443/test/test.xhtml?param=foo in my browser. As I expected the log claims that the view parameter was set to "foo". Now where I'm struggling is that when I first press the button the log again claims that param was set to "foo" proving that the setter was called again. I do not understand why the view parameter is set again by the ajax request. It also puzzles me that any subsequent button click will not call the view parameter's setter again, especially as the first and all subsequent calls look exactly alike.
So my questions are:
Why is the view parameter's setter called on the first ajax call but not on subsequent calls?
Is there any way to prevent this behavior?
I'm running the example on Wildfly 19 which uses Mojarra 2.3.9.SP06 if that is of any help.
EDIT 1: To make it clearer, why this question is different from f:viewParam lost after ajax call. The other question asks why the view parameters are lost after the first ajax call and how to always send them. This is question asks exactly the opposite: Why are the view parameters send the first time anyway and how to prevent this?
The answer to the other question claims that one can call FacesContext.getCurrentInstance().isPostback(). I'm aware of this. While it of course works in the sense of detecting the ajax recall and enables me to not reset the view parameters in this case it does not prevent the view parameter's setter from being called in the first place. This is what I ideally want to achieve. I would also content myself with at least understanding why the view parameters are treated differently on the first ajax call. I guess there is something conceptually I have not understood.
EDIT 2: I filed a bug report under https://github.com/eclipse-ee4j/mojarra/issues/4714.

There is nothing you conceptually misunderstood. I don't understand it either.
I'm currently still investigating on the why the setter is called on the first and only on the first ajax callback. I would have expected it to be always or never called. The analysis of #fuggerjaki61 is somewhat in the right direction but it seems to be related to the bigger issue around null or not submitted values.
Lots of info can be read in what is the easiest solution: the OmniFaces o:viewParam instead of f:viewParam
And use
<o:viewParam id="param" name="param" value="#{testController.param}"/>
(do not forget to declare xmlns:o="http://omnifaces.org/ui", but since you should ;-) be using OmniFaces anyway, I assume it is already there :-) )
From al info I read I thought that maybe setting
<context-param>
<param-name>javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL</param-name>
<param-value>true</param-value>
</context-param>
might solve it as well, but it does not. The setter is still called with the old value on the first ajax call and on the second and subsequent calls it only explicitly sets the value to null if it is not submitted. Also not what you seem to be wanting.
More details
The solution of #fuggerjaki61 might work, but I'm not sure about the consequences in other situations, since I can also get a fix for this issue by changing other things but breaking other cases. And if I try to compare the basics of o:viewParam with f:viewParam the submitted value (as referred to by #fuggerjaki61 in the other answer) does play a role. It is kept local in the o:viewParam
#Override
public String getSubmittedValue() {
return submittedValue;
}
#Override
public void setSubmittedValue(Object submittedValue) {
this.submittedValue = (String) submittedValue; // Don't delegate to statehelper to keep it stateless.
}
while in the f:viewParam it is being read from and set to the stateHelper
#Override
public Object getSubmittedValue() {
return getStateHelper().get(PropertyKeys.submittedValue);
}
/**
* PENDING (docs) Interesting that submitted value isn't saved by the parent
* #param submittedValue The new submitted value
*/
#Override
public void setSubmittedValue(Object submittedValue) {
getStateHelper().put(PropertyKeys.submittedValue, submittedValue);
}
Reading the java docs here I'd personally say on the "why" in your question to me looks like there is a bug (or omission) somewhere, yet to be identified, but either accidentilly or explicitly solved by o:viewParam

Quick Solutions
The best way to solve this problem is using the o:form with includeViewParams set to true (setParam called on every ajax request; only way when parameters can change in ajax requests).
OR
Already said by #Kukeltje using the o:viewParam (that does the same like overriding the UIViewParameter), so the setParam method is only called once at the beginning.
Explanation
Basically is the parameter value saved during the initial request to the first ajax request. After first ajax request the value is finally lost.
Probably the best way to understand this is to analyse phase for phase (looking at the source code to see what the methods do is also helpful):
Initial Request
Restore View Phase: nothing specific
Apply Request Values Phase: decode called and rawValue is set with the current parameter value
Process Validations Phase: nothing specific
Update Model Values Phase: setParam is called and after that UIInput.resetValues() that sets the submittedValue to null
Invoke Application Phase: nothing specific
Render Response Phase: setSubmittedValue (which was null) is called with rawValue (rawValue was already set; see Apply Request Values Phase)
First Ajax
Restore View Phase: rawValue is reinitialized to null
Apply Request Values Phase: decode called and rawValue is set with the current parameter value (parameter value is null)
Process Validations Phase: nothing specific
Update Model Values Phase: setParam is called with the submittedValue that was set to null but then set again in Render Response Phase; UIInput.resetValues() is called again and submittedValue is set to null.
Invoke Application Phase: nothing specific
Render Response Phase: setSubmittedValue is again called and set to rawValue which is null
Every following ajax request
submittedValue and rawValue is null so every possibility to restore the parameter value is destroyed. setParam is never called again.
All Solution
Overriding the encodeAll method to do nothing anymore, so UIInput.resetValues() resets values forever (see how to override components)
Using o:viewParam (doesn't have rawValue variable)
Using o:form
When the parameters don't change during ajax requests the top two solutions are the best.
Overriding UIViewParameter
To override the UIViewParameter create a class that extends the UIViewParameter and add this to the faces-config.xml:
<component>
<component-type>javax.faces.ViewParameter</component-type>
<component-class>com.example.CustomUIViewParameter</component-class>
</component>

Related

Spring controller, why is the returned view ignored?

So, say I have an existing, working page Display Cashier, which displays information about a cashier in a shop. Now, I add a button to this page that looks like:
Manager
The request-mapping for this URL maps it (successfully) to a controller: HandleGetManager
the HandleGetManager controller looks like this:
#Controller
public class HandleGetManager{
private employeeBO employeeBO; //BO handles all business logic
//spring hooks
public HandleGetManager(){}
public void setemployeeBo(employeeBO employeeBO){
this.employeeBO = employeeBO;
}
//get controller
#RequestMapping(method=RequestMethod.GET)
public String getManager(#RequestParam String cashierId){
Long managerId = employeeBO.getManagerByCashierId(cashierId);
String redirectUrl = "/displayManager.ctl?managerId=" + managerId.toString();
return redirectUrl;
}
}
Here's what happens when I try it:
I hit the new button on the Display Cashier page, I expect the following to happen:
The browser sends a get request to the indicated URL
The spring request-mapping ensures that the flow of control is passed to this class.
the #RequestMapping(method=RequestMethod.GET) piece ensures that this method is evoked
The #RequestParam String cashierId instructs Spring to parse the URL and pass the cashierId value into this method as a parameter.
The EmployeeBo has been injected into the controller via spring.
The Business logic takes place, envoking the BO and the managerId var is populated with the correct value.
The method returns the name of a different view, with a new managerId URL arg appended
Now, up until this point, everything goes to plan. What I expect to happen next is:
the browsers is directed to that URL
whereupon it will send a get request to that url,
the whole process will start again in another controller, with a different URL and a different URL arg.
instead what happens is:
this controller returns the name of a different view
The browser is redirected to a half-right, half wrong URL: handleGetManager.ctl?managerId=12345
The URL argument changes, but the name of the controller does not, despite my explicitly returning it
I get an error
What am I doing wrong? Have I missed something?
Assuming you have a UrlBasedViewResolver in your MVC configuration, the String value you return is a View name. The ViewResolver will take that name and try to resolve a View for it.
What you seem to want to do is to have a 301 response with a redirect. With view names, you do that by specifying a redirect: prefix in your view name. It's described in the documentation, here.
Here's a question/answer explaining all the (default) ways you can perform a redirect:
How can I prevent Spring MVC from doing a redirect?

MVC5 custom HandleErrorAttribute needs to detect whether to return partial view to ajax call

I've created a custom HandleErrorAttribute in order to implement logging as part of the exception handling. However, unrelated to the logging, I have run into an issue where I need to render a partialview on an Ajax request instead of a JsonResult. I can detect that it is an ajax request but I can't figure out how to determine when it is appropriate to generate a JsonResult or a PartialView. In most instances, a JsonResult is appropriate but in some cases a PartialView is appropriate. Is there a way to determine what the action is expecting from within OnException()?
I was hoping to find a way to detect the required type of response via a property in filterContext or something that would allow me to dynamically determine the expected response type. In all of my research, I could not find anything that would make that possible. So, I dealt with the situation by adding a bool property (defaulting to false) to the custom HandleErrorAttribute class. I then applied this attribute to the method that is responding with a partialView instead of a JsonResult, setting the property value to true. When the property value is true, the OnException() method responds with a partialView instead of a JsonResult.
If there is a better way, please let me know.
I think the following snippets could help you detect the required type of response
protected override void OnException(ExceptionContext filterContext)
{
//Determine the return type of the action
string actionName = filterContext.RouteData.Values["action"].ToString();
Type controllerType = filterContext.Controller.GetType();
var method = controllerType.GetMethod(actionName);
var returnType = method.ReturnType;
if (returnType.Equals(typeof(JsonResult)))
{
}
}

Loop in Spring Controller Method

I have some very strange behaviour in one of my spring controller which I can not explain.
So this is what I have. Very simple controller.
#RequestMapping(value="/doSomething")
public void doSomething(#RequestParam int value, HttpSession session) {
System.out.println("Lorem");
// Some stuff later on done here
System.out.println("ipsum");
}
When I request the mapped URL the controller behaves as it whould have an infiniate loop inside. So it starts with the output "Lorem" then "ipsum" and instead of leaving the method it starts right from the beginning of the method again. It is not called multiple times from external. Does anybody know this behaviour or has any clue? Furthermore I could observe that the output speed slows down as memory drastically increases up to about 1.5 GB and 100% CPU usage spread all over every single core.Thanks for your help.
Your handler method doesn't seem right to me. Typically you need to return a string that will be resolved into a View by ViewResolver, eg:
#RequestMapping(value="/doSomething")
public String doSomething(#RequestParam int value, HttpSession session) {
// ....
return "somethingdone";
}
This is my understanding.
If a controller declares a void return type , Spring will attempt to infer the view name from the request URL.
In your case, it will assume the view name is "doSomething", and proceed on that assumption.
It does this using an implementation of RequestToViewNameTranslator, The default implementation of RequestToViewNameTranslator is DefaultRequestToViewNameTranslator
See this

why does struts reset my form after failed validation?

I am using the validation framework with Struts 1.1.When validation fails, the entire form is reset.
After much time scouring the net, I have gathered:
When a new request is received, the form object is created if it does not exist in the current scope (request or session).
Reset is called()
Form values are populated from the bean properties.
Validation starts if enabled
If validation fails, ActionErrors are returned and the request is directed to the URI given by the input attribute of the action tag in my struts-config.xml.
That's where I have the problem. If validation fails, and I set the input param to the same page, reset() gets called again but it does not use the bean values from when the form is initially loaded. So the user has to re-enter everything.
My action mapping class for this action looks like this:
<action
path="/edit/componentRelease"
type="org.twdata.struts.SpringAction"
name="edit/componentRelease"
scope="request"
input="/WEB-INF/jsp/edit/editComponentRelease.jsp"
parameter="edit/componentRelease"
validate="true"
>
<forward
name="edit/componentRelease"
path="/WEB-INF/jsp/edit/editComponentRelease.jsp"
redirect="false"
/>
</action>
The form used to display the bean starts with:
<html:form method="post" name="componentReleaseEditor" type="com.mx.releasemgr.forms.ComponentReleaseEditorForm" action="/edit/componentRelease">
the reset() is used to clear the values previously entered...if u debug it and see then u'll come to know. eg ur entering 1 in the form and say submit and again come on the same form and enter 2 and submit again now what reset will do is it will clear 1 and now 2, and thus u get 2 in ur validate() part.
#Override
public void reset(ActionMapping mapping, HttpServletRequest request) {
//If Clear button click will set date as today and clear all other value
//If Insert, update with validation as failure than not clear all value on the form, but only clear that wrong when validation (means skipp all value as true on the form and only clear value wrong)
String actionName = request.getParameter("method");
if(actionName!=null){
if (actionName.equals(Enums.ActionName.CLEAR.getActionName())) {
Date date = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
this.setPublicationDay(dateFormat.format(date));
}
else{
request.setAttribute("book", this);
}
}
super.reset(mapping, request);
}
The solution is to move the display of the form to a different action than that used to forward. In my example, they are both the same.

Wrapper class does not save to database when set

I have a wrapper class that is meant to get and set code from a property in one of my dbml's partial classes. The reason for the wrapper is for a specialized get, which pre-formats the value. Here's what the wrapper looks like (NOTE: this is not the actual code, but represents everything but the formatting accurately):
partial class Class1
{
public string PropertyFormatted
{
get
{
var ret = Property.Substring(1);
return ret;
}
set { Property = value; }
}
}
This wrapper is bound using Bind() in a formview for the edit page. For some reason, the wrapper's value is set twice on update and the second time through the value is re-assigned its original value (causing the property to remain, ultimately, unchanged). However, when the wrapper is replaced with the property itself, there is no problem with saving to the database.
Any ideas what may be the cause of this?
The dbContext should be automatically detecting changes via this method:
http://msdn.microsoft.com/en-us/library/system.data.entity.infrastructure.dbchangetracker.detectchanges(v=vs.103).aspx
You may have inadvertently disable auto detect changes or something of the like. Try manually calling the method and see if that makes a difference.
Good luck!

Resources