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.
Related
I am trying to write a search function, but I encounter a bug when I pass the search query from frontend to backend. I tried most of the solution on Internet but it's still not ok.
Complete Error log
2022-10-12 15:05:10.575 WARN 21272 --- [nio-8090-exec-2] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.bind.MissingServletRequestParameterException: Required request parameter 'searchQuery' for method parameter type String is not present]
frontend
<template>
<div class="input-group mb-3">
<input type="search" class="form-control rounded" v-model="searchQuery" placeholder="Company name" aria-label="Search" aria-describedby="search-addon" />
<button type="button" class="btn btn-outline-primary" #click='searchRecord'>Search</button>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'RegistrationEnquiry',
components: {
},
data() {
return {
records: [],
searchQuery: '',
};
},
computed: {},
methods: {
searchRecord(){
axios.post('searchRecord', this.searchQuery)
.then(successResponse => {
console.log(successResponse)
})
.catch(failResponse => {
alert("Error(failResponse)")
console.log(failResponse)
})
},
},
}
</script>
SearchRecordController.java
#Controller
public class SearchRecordController {
#Autowired
SearchRecordService searchRecordService;
#CrossOrigin
#PostMapping(value = "api/searchRecord")
#ResponseBody
public String searchRecord(#RequestParam(value = "searchQuery") String searchQuery) {
System.out.println(searchQuery);
return searchRecordService.searchRecordService(searchQuery);
}
}
It is not the correct way of sending parameters through axios.
You can send params by changing your code in frontend to :-
axios.post(`api/searchRecord`, null, { params: {
searchQuery
}}
Which will send the request as :
https://localhost:8080/api/searchRecord?searchQuery=valueofsearchtext
Keep your controller as it is as there are no changes required in the backend.
#CrossOrigin
#PostMapping(value = "api/searchRecord")
#ResponseBody
public String searchRecord(#RequestParam(value = "searchQuery") String searchQuery) {
System.out.println(searchQuery);
return searchRecordService.searchRecordService(searchQuery);
}
this should sort the issue in your code.
From backend side it seems ok, I think you need to send data in post like that:
searchRecord(){
axios.post({
method: 'post',
url: '/api/searchRecord',
data: {
searchQuery: this.searchQuery
}
})
.then(successResponse => {
console.log(successResponse)
})
.catch(failResponse => {
alert("Error(failResponse)")
console.log(failResponse)
})
}
basically, it appears when you send no value from the front end but your controller expects some parameter from the frontend side,
to avoid this u can use "required = false" or u can fix a default value based on your requirement to check where you are going wrong.
public String searchRecord(#RequestParam(value = "searchQuery",required = false,defaultValue = "") String searchQuery) {
return searchRecordService.searchRecordService(searchQuery);
}
Front end is not sending data to the controller. Try giving default value=null;
I writing a simple Spring MVC web application that use JQuery DataTable on the client-side. DataTable using server-side processing mode. The DataTable make request with parameters(in my case):
draw:1
columns[0][data]:name
columns[0][name]:
columns[0][searchable]:true
columns[0][orderable]:true
columns[0][search][value]:
columns[0][search][regex]:false
columns[1][data]:type
columns[1][name]:
columns[1][searchable]:true
columns[1][orderable]:true
columns[1][search][value]:
columns[1][search][regex]:false
columns[2][data]:action
columns[2][name]:
columns[2][searchable]:true
columns[2][orderable]:true
columns[2][search][value]:
columns[2][search][regex]:false
order[0][column]:0
order[0][dir]:asc
start:0
length:10
search[value]:
search[regex]:false
I don't know how can i parse parameters like columns[i][data], columns[i][name], columns[i][searchable], etc. The reason is why, because I don't know how many table columns I will have. How to solve this problem?
This is my Spring controller:
#RequestMapping(value = "/getImageWrappers", method = RequestMethod.POST)
public String getImageWrappers(#RequestParam Integer draw,
#RequestParam Integer start,
#RequestParam Integer length,
#RequestParam(value = "search[value]") String searchText){
}
and DataTable configuration:
$('#imageWrapperTable').DataTable({
columns:[
{"data" : "name"},
{"data" : "type"},
{"data" : "action"}
],
"processing": true,
serverSide: true,
ajax: {
url: '/getImageWrappers.json',
type: 'POST'
}
});
I just figure it out by adding this code
$('#imageWrapperTable').DataTable({
columns:[
{"data" : "name"},
{"data" : "type"},
{"data" : "action"}
],
"processing": true,
serverSide: true,
ajax: {
url: '/getImageWrappers.json',
type: 'POST',
datatype: 'json',
data: function(d){
//add your custom param here
d.name = "zxbing";
//this will put all query strings to a json object string
return JSON.stringify(d);
}
}
});
In this way, you just need to transfer only a query string to JSON object on the server side.
In a json array I would do something like this with jquery:
function displayData(x) {
x.success(function(data) {
$.each(data.columns, function(i, paramenter) {
$.each(parameter, function(a, b) {
$('#somediv').append('column'+parameter+' -> data: '+b+' ');
});
});
}),
x.error(function(data) {
//error message
})
}//end displayData
So you can loop through your array, but you gotta play around with it, I haven't tested it but in general that is the idea. Hope it helps.
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>
I am trying to make a simple request using AJAX . But the whole thing is not working. Below is the code I wrote
jsp/javascript :
$("#my_"+rowNum).load("getdata.action?id="+123,function(data) {
alert("i am inside "+data);
});
Struts Action :
public class MyAction extends BaseAction {
public String execute() {
try {
inputStream = new ByteArrayInputStream("ABC 123 556".getBytes("UTF-8"));
}
catch (UnsupportedEncodingException e) {
//handle exception
}
return SUCCESS;
}
}
struts.xml :
<action name="getdata" class="com.amtd.advisoradmin.action.MyAction">
<result type="stream">
<param name="contentType">text/html</param>
<param name="inputName">inputStream</param>
</result>
I feel the configuration is correct, but I don't get the alert I printed in my jsp after the control returns from the Action class. Am I missing something ?
PS : ABC 123 556 is the data I need to get in the alerts.
I would suggest you to have a private variable for inputStream type InputStream and public getter and setter for it in your Action class, which is missing.
Thanks
I am getting the following error after submitting my Ext JS form:
Uncaught Ext.Error: You're trying to decode an invalid JSON String
JS:
Ext.onReady(function() {
var simple = Ext.create('Ext.form.Panel', {
frame : true,
title : 'Login Form',
bodyStyle : 'padding:5px 5px 0',
width : 350,
fieldDefaults : {
msgTarget : 'side',
labelWidth : 75
},
defaultType : 'textfield',
defaults : {
anchor : '100%'
},
items : [{
fieldLabel : 'User Name',
name : 'userName',
allowBlank : false,
emptyText : 'UserName'
}, {
fieldLabel : 'Password',
name : 'password',
allowBlank : false,
inputType : 'password',
emptyText : 'Password'
}],
buttons : [{
text : 'Save',
handler : function() {
var form = this.up('form').getForm();
form.submit({
url : saveFormUrl
// waitMsg : 'Sending the info...',
// success : function(fp, o) {
// Ext.Msg.alert('Success',
// 'Form submitted.');
// }
});
}
}, {
text : 'Cancel'
}]
});
simple.render(document.body);
simple.getEl().center();
});
Controller class:
#Controller
public class UserController {
private static final Logger logger = LoggerFactory
.getLogger(TController.class);
private TService tService = null;
#Autowired
public void setTService(TService tService) {
this.tService = tService;
}
#RequestMapping(value = "/index.html", method = RequestMethod.GET)
public String home() {
System.out.println("Welcome home!");
return "login";
}
#RequestMapping(value = "/save-form.html", method = RequestMethod.POST)
public ModelAndView submitData(User user){
System.out.println("User name:"+user.getUserName());
ModelAndView mv = new ModelAndView("htmlLinks");
return mv;
}
save-form.html:
<%# taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%# page session="false"%>
<c:set var="ctx" value="${pageContext.request.contextPath}" />
<html>
<head>
<title>POC</title>
</head>
<body>
Welcome User !!
</body>
</html>
What am I doing wrong? What is the solution? I am using Ext JS 4 and Spring MVC.
According to the documentation for form.submit, it looks like the response is required to be either JSON or XML, formatted like so:
{
success: true,
data: {
url: "http://somewhere",
someData: "whatever you want"
}
}
In your JavaScript's success handler, you can reference o.data.[variable] to get custom data.
Unfortunately, you will need to change the submitData method (in your controller) to return a JSON response object in the structure defined above. In the response object, you can include a URL to save-form.html. Then you can make an additional GET request for it in the success handler.
I don't know if this will work because I have no experience with Ext JS, but I would envision the success handler to look something like this:
success: function(fp, o) {
Ext.Msg.alert('Success', 'Form submitted.');
Ext.Ajax.request({
url: o.data.url,
method: "GET",
success: function(response, request) {
// do whatever you need to with the generated HTML
alert(response.responseText);
},
failure: function(response, request) {
alert('failed');
}
});
}
Thanks for all replies. I resolved it using the below code.
buttons : [{
text : 'Save',
handler : function() {
// The getForm() method returns the
// Ext.form.Basic instance:
var form = this.up('form').getForm();
form.submit();
}
}, {
text : 'Cancel'
}]