Found a strange issue in Struts action,
The scenario is like below ,
When I was in employees.jsp which just shows list of employees fetched from server , when an employee div is clicked, I will create a dynamic form as "employeeDetailsForm" in a javascript method, which has a hidden input field that holds value of employee code,
And I attached an action to the form as "getEmployeeDetails", when an employee div is clicked
It will submit the action and navigates to the employeeDetails.jsp,
In employeeDetails.jsp I will show set of details of that particular employee.
for his list of achievements I have upVote feature.
so when I click on upVote, upVote action gets called using ajax in a js method.
When upVote action is success , immediately again "getEmployeeDetails" action is getting called which is a strange behaviour.
I am not able to backtrace the call stack since many system defined methods are shown,
I just cross checked whether my "employeeDetailsForm" is exist in second jsp page as well and some how the form is being submitted somewhere ?
but when I checked inspector in browser, that form element was not there at all,
I also checked by calling document.getElementById("employeeDetailsFormId") which returned null
Then from where this method is getting called ?
How to trace this issue ?
Update -
Source code for reference
in employees.jsp - JS method to submit form to fetch details
var empDetForm = document.createElement("form");
empDetForm.id=“empDetailsId”;
empDetForm.name=“employeeDetailsForm";
empDetForm.method="POST";
empDetForm.action="getEmployeeDetails";
var empCodeinput = document.createElement("input");
empCodeinput.setAttribute("type","hidden");
empCodeinput.id= empCode;
empCodeinput.name=“empCode”;
empDetForm.appendChild(empCodeinput);
empDetForm.submit();
In employeeDetails.jsp - JS method to call upvote service using ajax
var formdata = "isUpVote="+isUpVote+”&”+”selectedAchievement="+achievementId;
$.ajax({
type: 'POST',
url: ‘achievementUpVote’,
contentType: "application/x-www-form-urlencoded",
async: false,
//processData:false,
data :formdata,
mimeType:"multipart/form-data",
cache: false,
processData:false,
datatype: "json",
success: function(response) {
alert("success upvote for achievement”);
},
error: function(e) {
alert("Fail upvote");
}
});
Java struts action methods
public String getEmployeeDetails() throws JSONException {
EmployeeDetailsIntf empDetailsImplObj = new EmployeeDetailsIml();
JSONObject jsonInput = new JSONObject();
String userName = ApplicationData.getUserName();
String accessCode = ApplicationData.getAccessCode();
jsonInput.put(“empId”, getEmpId());
jsonInput.put("un", userName);
jsonInput.put("ac", accessCode);
status = empDetailsImplObj.callGetEmpDetailsService(jsonInput);
if(status == true){
return "SUCCESS";
}
return "FAIL";
}
Another action
public String achievementRating() {
String userName = ApplicationData.getUserName();
String accessCode = ApplicationData.getAccessCode();
JSONObject jsonInputData = new JSONObject();
try {
jsonInputData.put("un", userName);
jsonInputData.put("ac", accessCode);
jsonInputData.put("aid", selectedAchivement);
EmployeeDetailsIntf empDetailsImplObj = new EmployeeDetailsIml();
boolean status = empDetailsImplObj.upVoteAchievement(jsonInputData, isUpVote);
if (status) {
return "RATE_ACHIEVEMENT_SUCCESS";
}else{
return "FAIL";
}
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null ;
}
struts.xml
<action name=“getEmployeeDetails” class="com.action.EmployeeDetailsAction"
method="getEmployeeDetails">
<result name="SUCCESS”>../empDetails/employeeDetails.jsp</result>
<result name="FAIL">failure.jsp</result>
</action>
<action name="achievementUpVote" class="com.action.EmployeeDetailsAction"
method="achievementRating">
<result name="RATE_ACHIEVEMENT_SUCCESS" type="json"/>
<result name="FAIL" type="json"/>
<result name="FAIL">failure.jsp</result>
</action>
Related
I am using Struts 2 (2.5.10.1) with struts2-json-plugin-2.5.10.1. On my page I have a grid using ExtJs 4.2.2 that will be populated with projects, each row will have two actions: edit and delete.
The problem is that I have encountered: if I try to use multiple methods from the same action to do all the above, when I try to populate the grid I get no JSON response. Is what i am trying to do even possible ? (The edit and delete functionalities are not yet implemented)
Here is my struts.xml:
<package name="admin" extends="json-default">
<action name="requestProjectData"
class="administrator.ACTION_ProjectsGrid"
method="requestProjectData">
<result type="json" >
<param name="root">projectData</param>
</result>
</action>
<action name="requestDeleteProject"
class="administrator.ACTION_ProjectsGrid"
method="deleteProject">
<result type="json" >
<param name="root">success</param>
</result>
</action>
<action name="requestEditProject"
class="administrator.ACTION_ProjectsGrid"
method="editProject">
<result type="json" >
<param name="root">success</param>
</result>
</action>
Here is my Action class:
public class ACTION_ProjectsGrid extends ActionSupport {
private static final long serialVersionUID = 1L;
private List<Project> projectData;
private boolean success;
public void requestProjectData()
{
ProjectManager pm = new ProjectManager();
List<Project> listOfProjects = pm.getAllProjects();
projectData = listOfProjects;
}
public void deleteProject()
{
success = true;
}
public void editProject()
{
success = true;
}
public List<Project> getProjectData() {
return projectData;
}
public void setProjectData(List<Project> projectData) {
this.projectData = projectData;
}
public boolean isSuccess() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
}
And my JSP page:
<body>
<script type="text/javascript">
Ext.define('Project', {
extend: 'Ext.data.Model',
fields: [ 'projectId', 'projectName' ]
});
var projStore = Ext.create('Ext.data.Store', {
model: 'Project',
proxy: {
type: 'ajax',
pageParam: false,
startParam: false,
limitParam: false,
noCache: false,
url: 'requestProjectData',
reader: {
type: 'json'
}
},
autoLoad: true
});
Ext.create('Ext.grid.Panel', {
renderTo: Ext.getBody(),
store: projStore,
width: 641,
height: 300,
title: 'Projects',
columns: [
{
text: 'ID',
width: 80,
dataIndex: 'projectId'
},
{
text: 'Project Name',
width: 80,
dataIndex: 'projectName'
},
{
xtype: 'actioncolumn',
width: 80,
items: [{
icon: '${pageContext.request.contextPath}/JavaScript/extjs/resources/user_edit.png',
tooltip: 'Edit',
handler: function(grid, rowIndex, colIndex) {
var rec = grid.getStore().getAt(rowIndex);
Ext.Ajax.request({
url: 'requestDeleteProject',
disableCaching: false,
params: {
projectId: rec.get("projectId").toString(),
},
success: function(response)
{
grid.getStore().load();
}
});
}
}]
}
]
});
</script>
</body>
Ext.Ajax.request({
url: 'requestDeleteProject',
disableCaching: false,
params: {
projectId: rec.get("projectId").toString(),
},
success: function(response){
grid.getStore().load();
}
});}
Inside that part you making your request... You should decode the "response" inside the sucess and after it load the data to your store; making grid.getStore().load()
You are making another Request you want is something like
var json = Ext.decode(response.wtv..);
and then you want to load the grid's store like
grid.getStore().loadData(json);
soo like
success: function(response){
var json = Ext.decode(response.wtv..);
grid.getStore().loadData(json);
}
I found the problem. For this solution to work, all methods have to be created like the execute method. After I changed the return type to String and returned ActionSupport.SUCCESS, the json response appeared. But I still do not know if this solution is correct or I am breaking some rules, can anyone explain this to me ?
If you define an action that is mapped to the action class' method, the method should return either String or Result. The ActionSupport class provides you the default implementation for the action class.
Beside that, it implements a lot of interfaces that involve additional functionality via interceptors. One of this interfaces is Action. It is a functional interface that has only one method execute() which is invoked by default if no method is mapped to the action.
The value that is returned from the action method is a result code. This code or Result is necessary for Struts to proceed with the response after the action was executed. Thus the result code is returned to the invoker (ActionInvocation) which finds and executes a result with the same name defined in the action configuration. The default result name is Action.SUCCESS or ActionSupport.SUCCESS that you can return from the action. There should be result in the action configuration that implicitly or explicitly define a result with the name "success".
The "success" result is defined by default in your configuration, because it's missing name attribute. So to execute this result you should return "success", or Action.SUCCESS, or ActionSupport.SUCCESS that are the same string.
See more in the Result Configuration.
When an action class method completes, it returns a String. The
value of the String is used to select a result element. An action
mapping will often have a set of results representing different
possible outcomes. A standard set of result tokens are defined by the
ActionSupport base class. Predefined result names
String SUCCESS = "success";
String NONE = "none";
String ERROR = "error";
String INPUT = "input";
String LOGIN = "login";
Of course, applications can define other result tokens to match
specific cases.
I am using jquery Ajax to send my parameters to backend in java and return value is of JSON type. My application has different categories which uses the same jsp. If i open a single category in one tab everything is working fine. But when i open different categories in different tabs, the last opened tab/category only sends parameters to the back end, the first opened tabs triggers the ajax call, but the parameters passed are not available in the back end. PFB the code snippet of the AJAX call made,
function addThings(things) {
$(document).ready(function() {
var parameters = {
_method : 'put'
};
for ( var i = 0; i < things.length; i++) {
if (parameters[things[i][0]] != null) {
parameters[things[i][0]] = parseInt(parameters[things[i][0]])
+ parseInt(things[i][1]);
} else {
parameters[things[i][0]] = things[i][1];
}
}
$.ajax({
type : "POST",
url : "addThings.do",
async : false,
data : parameters,
datType : "json",
failure : function(data) {
ShowFatalError();
},
success : function(response) {
var resp =$.getJSON(response);
if (resp == null) {
ShowFatalError();
} else {
if (response.exceptionsOccured) {
ShowFatalError();
}
CurrentJSON = response;
CountDisplay(CurrentJSON);
return (CurrentJSON);
}
}
});
});
return CurrentJSON;
}
The above function is triggered on a button click.
Please help me out in this.
I am new to Spring and web technology.
I have an table which contains a column with hyperlink. When I click on the hyperlink of a row, I need to display that rows data along with other details in a dialog. My controller method returns a ModelAndView which contains the data I need to show and the display page.
Problems:
How to show the dialog? and
How to pass the data to the dialog?
Table.jsp
<script type="text/javascript">
function showDialog(ref, date) {
$ajax({
type: "POST",
url: "/example/show.htm",
data: {
ref: ref,
date: date
}
success: function(data) {
},
error: function(data) {
}
});
}
</script>
Mapping
#RequestMapping(value = "show.htm", method=RequestMethod.POST)
public ModelAndView show(#RequestParam("ref") String ref, #RequestParam("date") String date,
HttpServletRequest request, HttpServletResponse response) {
ModelAndView modelAndView = new ModelAndView();
try {
SampleDTO SampleDTO = new SampleDTO();
sampleDTO.setDate(sdf.parse(date));
sampleDTO.setRef(ref);
SampleDTO billDto = // server call modelAndView.addObject("showBill", sampleDto);
modelAndView.setViewName("Dialog");
}
return modelAndView;
}
Your code is wrong, you are messing things, if you want to use jQuery and ajax calls then don't use ModelAndView in your Spring controller. Instead of that, use the following and return your bean or dto as a json using Jackson library from Java:
Include this jar in your lib project folder:
http://www.java2s.com/Code/JarDownload/jackson/jackson-all-1.9.9.jar.zip
Java code:
#RequestMapping(value = "businessBill.htm", method = RequestMethod.POST)
#ResponseBody
public String handleBusinessBillDetails(#RequestParam("reference") String billReference, #RequestParam("invoiceDate") String billDate,
HttpServletRequest request, HttpServletResponse response) {
String json = null;
try {
//1. Create 'jackson' object mapper
ObjectMapper objectMapper = new ObjectMapper();
BusinessBillDTO businessBillDTO = new BusinessBillDTO();
businessBillDTO.setBillDate(sdf.parse(billDate));
businessBillDTO.setBillReference(billReference);
BusinessBillDTO billDto = accountStatementBO.getBusinessBillDetails(businessBillDTO);
//2. Convert your 'bean' or 'dto' as 'json' string
json = objectMapper.writeValueAsString(billDto);
} catch (Exception ex) {
LOGGER.error(ex);
}
return json;
}
Then, in Table.jsp put the div used in Dialog.jsp as hidden, this will be your modal dialog in future (note that there are some changes in the span tags also):
<div id="BusinessBill" style="display:none;">
<h2>Bill Details</h2>
<em>Business Ltd</em>
<div class="row">
<span class="spanAsLabel">Account number</span>
<span id="dlg-account-number" class="spanAsLabel"></span>
</div>
<div class="row">
<span class="spanAsLabel">Bill date</span>
<span id="dlg-bill-date" class="spanAsLabel"></span>
</div>
</div>
Now fix your getBusinessBill(..) method like this:
You can also use $.ajax and maybe handle more states like onerror and others but this way is simpler (at least for me, you just need to evaluate if the returned data is null or not and let know the user - if null - that something happened at server side, maybe showing an alert with a generic message) - please read comments.
function getBusinessBill(billReference, billInvoiceDate) {
$.post("/AccountStatement/businessBill.htm", {
reference: billReference,
invoiceDate: billInvoiceDate
}, function (data) {
/* You can implement more validations for 'data', in my case I just used these 'if' conditionals but can vary. */
if(data != null) { //returned 'data' is not 'null'
/* parse 'data' as 'json' object
* will be good to console.log(data) and take a look. */
var obj = $.parseJSON(data);
if(obj != {}) { //check if 'data' is not an empty 'json' object once transformed
//set the 'data' in the dialog
$('#dlg-account-number').text(obj.accountNumber);
$('#dlg-bill-date').text(obj.billDate);
/* open modal dialog, you can simulate it this way (for this case)
* but the correct way is to use 'jquery-ui' dialog or any plugin you prefer.
* At this point you will see the hidden 'div' in a visible way with your 'data'.
*/
$('#BusinessBill').fadeIn();
} else {
//show 'generic' message
alert('No results found.');
}
} else {
//show 'generic' message
alert('An error occurred, try again.');
}
});
}
Finally, if everything is correct, you will see at the same page (Table.jsp) the modal dialog with your data, all made by an ajax call to avoid redirection pages like (Table.jsp to => Dialog.jsp).
I am calling two java script in onChange which are called two different action of struts.
Code follows:
<html:select property="countryid" onchange="retrieveURL('showStates.do?country=' + this.value);retrieveURL2('showStatesNotinGroup.do?country=' + this.value);">
function retrieveURL(url)
{
if(window.XMLHttpRequest)
{
// Non-IE browsers
req = new XMLHttpRequest();
req.onreadystatechange = processStateChange;
try {
req.open("GET", url, true);
} catch (e) {
alert(e);
}
req.send(null);
} else if (window.ActiveXObject) { // IE
req = new ActiveXObject("Microsoft.XMLHTTP");
if (req) {
req.onreadystatechange = processStateChange;
req.open("GET", url, true);
req.send();
}
}
}
function processStateChange() {
if (req.readyState == 4) { // Complete
if (req.status == 200) { // OK response
document.getElementById("box2View").innerHTML = req.responseText;
} else {
alert("Problem: " + req.statusText);
}
}
}
function retrieveURL2(url)
{
if (window.XMLHttpRequest) {
// Non_IE broeser
req = new XMLHttpRequest();
req.onreadystatechange = processCityChange;
try {
req.open("GET", url, true);
} catch (e) {
alert(e);
}
req.send(null);
} else if (window.ActiveXObject) {
//IE
req = new ActiveXObject("Microsoft.XMLHTTP");
if (req) {
req.onreadystatechange = processCityChange;
req.open("GET", url, true);
req.send();
}
}
}
function processCityChange(){
if (req.readyState == 4) { //Coplete
if (req.status == 200) { // OK responce
document.getElementById("box1View").innerHTML = req.responseText;
}else {
alert("Problem: " + req.statusText);
}
}
}
For this action mapping is:
<action path="/showStates" type="com.dss.action.ShowStatesAction" validate="false" name="stateForm">
<forward name="success" path="/showStates.jsp"/>
</action>
<action path="/showStatesNotinGroup" type="com.dss.action.ShowStatesAction" validate="false" name="stateForm">
<forward name="success" path="/showStatesNotInGroup.jsp"/>
</action>
</action-mappings>
When I run it one by one for checking it works fine, but when I call it together it's giving me an unexpected result.
I want to call first java script and check whether it's successful and then call the second one on same onChange.
You need to declare your req variable scoped to each function otherwise both functions are using the same global variable. You might also look at using a framework, such as jQuery, to do this as you'll have well-tested, browser-independent code with less effort on your part.
function retrieveURL(url)
{
var req; // <-- declare local so the scopes don't conflict
if(window.XMLHttpRequest)
{
...
}
function retrieveURL2(url)
{
var req; // <-- declare local so the scopes don't conflict
if(window.XMLHttpRequest)
{
...
}
And with jQuery
<script type="text/javascript" src=...jquery_location, local or via Google CDN
<script type="text/javascript">
$(function() {
$('#countryid').on('change', function() { // single handler for both
var $this = $(this), // cache jQuery object for later use
val = $this.val(); // cache value
$.get('showStates.do?country=' + val, function(result) {
$('#box2View').html(result);
});
$.get('showStatesNotinGroup.do?country=' + val, function(result) {
$('#box1View').html(result);
});
});
});
</script>
i found the way to do it and its work fine so i share you my way
<html:select property="countryid" onchange="retrieveURL(this.value);">
and the java script is like
function retrieveURL(url){
var newUrl = 'showStates.do?country='+url;
// do some thing
retrieveURL2(url);
}
function retrieveURL2(url){
var newUrl2 = 'showStatesNotinGroup.do?country='+url;
// do same thing
}
I have a web application with HTML / jQuery which ic connected with AJAX / JSON to a backend system with Java EE / Spring MVC.
In the frontend, a Person can be created by fill in the form fields and then it is submitted and this jQuery code executed:
var person = $(this).serializeObject();
$.postJSON("add/", person, function(data) {
alert("Person with ID "+data.person.id+"' added successfully");
});
In the best case, the Person is created and I'll get a Person object and I can access the values with data.person.*.
Now I want to validate the data which is sent to the backend system and in a case of an error, I want to display in the first step an alert error message.
I did this in the backend system:
#RequestMapping(value="add/", method=RequestMethod.POST)
public #ResponseBody Map<String, ? extends Object> addPerson(#RequestBody Person p, HttpServletResponse response) {
Set<ConstraintViolation<Person>> failures = validator.validate(p);
if (!failures.isEmpty()) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
return validationMessages(failures);
} else {
Person person = this.personService.addPerson(p);
return Collections.singletonMap("person", new SerialPerson(person.getId(), person.getName(), ...));
}
}
// internal helpers
private Map<String, String> validationMessages(Set<ConstraintViolation<Person>> failures) {
Map<String, String> failureMessages = new HashMap<String, String>();
for (ConstraintViolation<Person> failure : failures) {
failureMessages.put(failure.getPropertyPath().toString(), failure.getMessage());
System.out.println(failure.getPropertyPath().toString()+" - "+failure.getMessage());
}
return failureMessages;
}
My Person object is annotated, and I get the System.out.println(failure.getPropertyPath().toString()+" - "+failure.getMessage()); on the console, that for example, "name - must be between 1-30 chars"
But how can create an alert message in jQuery in the frontend system?
Thank you in advance for your help & Best Regards.
Update: Link to the Spring MVC AJAX example, where I found the validationMessages method. But there is also no solution how to get the error message.
SOLUTION:
I have to call:
jQuery.ajax({
'type': 'POST',
'url': "add/",
'contentType': 'application/json',
'data': JSON.stringify(person),
'dataType': 'json',
'success': function(data) {alert("success");},
'error': function(xhr) {alert(xhr.responseText);}
});
You can do something like this:
var person = $(this).serializeObject();
$.postJSON("add/", person, function(data) {
if(data.person) {
alert("Person with ID "+data.person.id+"' added successfully");
}
else {
var errors = "";
for(var key in data) if(data.hasOwnProperty(key)) {
errors += data[key] + "\n";
}
alert(errors);
}
});
You shouldn't need to send back a bad request either. Is this what you want?
UPDATE
You can use the code shown in Spring Source, but you'd have to use jQuery.ajax
jQuery.ajax({
type: 'POST',
url: "add/",
data: person,
dataType: "json",
success: function(data) {
alert("Person with ID "+data.person.id+"' added successfully");
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
var errorJSON = JSON.parse(XMLHttpRequest.responseText); //if this is JSON otherwise just alerting XMLHttpRequest.responseText will do
var errors = "";
for(var key in errorJSON) if(errorJSON.hasOwnProperty(key)) {
errors += errorJSON[key] + "\n";
}
alert(errors);
}
});