Spring MVC modelAndView object in javascript - java

Problem:
I have a simple controller which returns me a list of hardcoded locations. When i want to get my locations in my javascript file using $.get and print it in my console, i get some weird "undefined" results.
Controller:
#RestController
#RequestMapping("/locationOverview")
public class LocationOverviewController {
private LocationGuide service;
public LocationOverviewController() {
this.service = new LocationGuide("Memory");
}
#RequestMapping(method = RequestMethod.GET)
protected ModelAndView getLocations() {
ArrayList<Location> locations = new ArrayList<Location>();
locations.add(new Location(1,"KHL",new Geolocation(51,51)));
locations.add(new Location(2,"KUL",new Geolocation(51,51)));
return new ModelAndView("locationOverview", "locations", locations);
}
}
mapScript JS:
function initialize() {
$.get("locationOverview.htm", function(data){
for(var i=0; i<data.length; i++){
console.log(data[i].name);
}
})
}
JSP file:
<%#page contentType="text/html" pageEncoding="UTF-8"%>
<%#page import="domain.Location"%>
<%#taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Leuven Speaks</title>
<script type='text/javascript' src='http://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js'></script>
<script type="text/javascript" src="<c:url value="/js/mapScript.js" />"></script>
</head>
<body onload="initialize()">
<jsp:include page="header.jspf"/>
</body>
</html>

You should actually inspect with dev tools what that data contains. The controller method you show us looks like it is not the one mapped to locationOverview.htm. If you want to query data with ajax, return the list using the #ResponseBody annotation instead.
Data passed in a model would be available in the JSP page that you define as the view in ModelAndView.
Some learning resources:
http://www.beingjavaguys.com/2014/05/json-response-with-responsebody_31.html
https://developer.chrome.com/devtools/docs/javascript-debugging

Related

How to open Spring ModelAndView response in Ajax success method as a complete page

I am unable to open ModelAndView response given by Spring controller method using Ajax success method.
Spring Controller method:
#RequestMapping (value="setupDbEdit.htm", method=RequestMethod.GET)
ModelAndView setupABC(#RequestParam String db_id){
System.out.println("**hello world**"+db_id);
ModelAndView modelAndView=new ModelAndView("editDB");
request.setAttribute("dbID", db_id);
return modelAndView;
}
#RequestMapping (value="editDB.jsp", method=RequestMethod.GET)
ModelAndView addCustomerSetupABC(){
ModelAndView modelAndView= new ModelAndView("editDB");
return modelAndView;
}
Ajax code:
$("#editDb").click(function(e){
$.ajax({
type: 'GET',
'url': 'http://localhost:8080/Test_ReportingUI/setupDbEdit.htm',
data: {db_id: dbID},
dataType: 'html',
success: function(response){
alert(response);
},
timeout: 10000,
error: function(xhr, status, err){
}
});
});
editDB.jsp (The page that I want to open):
<%# page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<script>
alert("hellow");
</script>
<%
String strDBID=(String) request.getAttribute("dbID");
out.println(strDBID+"<br><br>");
%>
This is edit screen for DB details...
</body>
</html>
I don't want to open this page in pop up, rather I want to get it open on same page replacing the old.
On the same page you should specify the target where the returned html text should be injected. For example
<div id="result"/>
then on success handler load the content
success: function(response){
$("#result").html(response);
},

spring doesn't execute javascript

I use Spring MVC 3 in my project.
This is my AddressController :
#Controller
public class AddressController {
private static Logger logger = Logger.getLogger(AddressController.class);
#RequestMapping(value="/address",method=RequestMethod.GET)
public ModelAndView init(
#RequestParam(value="language",required=false,defaultValue="fr") String language){
Locale locale = new Locale(language);
logger.info("here");
String[] isoCountries = locale.getISOCountries();
Map<String,String> treeMap = new TreeMap<String,String>();
for(String isoCountry : isoCountries){
Locale countryLoc = new Locale(language, isoCountry);
String name = countryLoc.getDisplayCountry(locale);
if(!"".equals(name)){
treeMap.put(name,name);
}
}
Map<String,String> tree = new TreeMap<String,String>(treeMap);
ModelAndView modelAndView = new ModelAndView("address");
modelAndView.addObject("address",new Address());
modelAndView.addObject("countriesList", tree);
return modelAndView;
}
}
The first time, when I executed /address, it's going well to my controller and it returns my address.jsp by executing the javascript in this last one. But when I execute /address?language=fr or /address?language=en, the javascript code of my address.jsp is not executed.
This is a part of my address.jsp :
<%#page import="org.springframework.context.i18n.LocaleContextHolder"%>
<%# taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%# taglib prefix="forms" uri="http://www.springframework.org/tags/form" %>
<%#page import="com.application.myGoogleAppEngine.Internationale"%>
<%# page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%# page import="java.util.Locale" %>
<%# page import="java.util.List" %>
<%# page import="java.util.ArrayList" %>
<%# page import="java.util.Collections" %>
<html>
<head>
<jsp:include page="ressources.jsp"></jsp:include>
<link rel="stylesheet" type="text/css" href="stylesheets/320x480/portrait/address.css" />
<%! Internationale internationale = Internationale.getInstance(); %>
<script>
$(document).ready(function(){
//checkParams();
alert("here");
var unit = "em";
alert("here2");
$("#backButton").attr("href","/index");
$('#validationBtn').click(function(){
var streetName = $('#streetName').val();
var streetNumber = $('#streetNumber').val();
var zipCode = $('#zipCode').val();
var city = $('#city').val();
var country = $('#country').val();
var ref = "MyServlet?streetName="+streetName+"&streetNumber="+streetNumber+"&zipCode="+zipCode+"&city="+city+"&country="+country;
$(this).attr("href",ref);
});
});
//rest of the script
</script>
<body>
<a id="backButton" data-role="button" data-icon="arrow-l"
data-ajax="false">
<spring:message code="backButton"/>
</a></div>
//rest of the code
</body>
In web MVC spring serves as server side technology. Javascript is client (browser) side technology, thus Spring "cannot" execute javascript.

Output java variable value to be parsed in JS

I have this code:
<script type="text/javascript">
var endpoint = "http://localhost:8080/LWP/in.jsp?code=" + "<% out.println(request.getParameter("code")); %>";
window.opener.location.href = endpoint;
window.close();
</script>
What I expect it to do is redirect the page which opened the browser window this is processing in to
http://localhost:8080/LWP/in.jsp?code=<code here>
If I remove the <% out.println() %> portion of the script, it works fine, and I am redirected as expected (minus the value being passed in).
What am I doing incorrectly with the output of the parameter?
I also tried removing out.println. Still does not work.
Figured it out. Had to assign the value to a new variable, then use that.
<%#page contentType="text/html" pageEncoding="UTF-8"%>
<% String code = request.getParameter("code"); %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>JSP Page</title>
<script type="text/javascript">
var code = "<%=code%>";
var endpoint = "http://localhost:8080/LWP/in.jsp?code=" + code;
window.opener.location.href = endpoint;
window.close();
</script>
</head>
<body>
</body>
</html>

How do I reset a ModelAttribute in Spring 3?

I just started with Spring MVC so it's probably a rookie mistake.
The ModelAttribute is reused every request. How can I make sure every POST starts with a clean object?
My controller (MyController.java):
#Controller
public class MyController {
#RequestMapping(method = RequestMethod.POST)
public String processChoice(#ModelAttribute("myData") MyData myData, BindingResult bindingResult) {
System.out.println("POST: myData = " + myData);
return "redirect:/myview?choice=" + myData.getChoice();
}
#RequestMapping(method = RequestMethod.GET)
public String displayChoice(#RequestParam(required = false) String choice, Model model) {
System.out.println("GET: Choice = " + choice);
model.addAttribute("myData", new MyData(choice));
return "myview";
}
}
My view (myview.jsp):
<%# page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%# taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%# taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Spring Tests</title>
</head>
<body>
<form:form modelAttribute="myData" method="POST">
<form:select path="choice">
<option></option>
<option value="one">One</option>
<option value="two">Two</option>
</form:select>
<input type="submit" value="Choose"/>
</form:form>
<c:if test="${not empty myData.choice}">Choice = ${myData.choice}</c:if>
<c:if test="${empty myData.choice}">No choice</c:if>
</body>
</html>
Successive clicks on the "Choose" button appends the chosen values instead of just POST-ing the current one:
GET: Choice = null
POST: myData = MyData [choice=two]
GET: Choice = two
POST: myData = MyData [choice=two,one]
GET: Choice = two,one
POST: myData = MyData [choice=two,one,]
GET: Choice = two,one,
POST: myData = MyData [choice=two,one,,one]
GET: Choice = two,one,,one
Add this into your controller:
#ModelAttribute("myData")
public MyData getMyData() {
return new MyData();
}

Spring MVC and form binding : how to remove an item from a List?

I have a Person model attribute that contains a list of emails.
I've created some JavaScript code that deletes elements from an HTML list of emails. This is pure JavaScript client side code, no AJAX call.
After submitting, I don't understand why I get all the emails in the corresponding #Controller method, even the ones that were deleted in the HTML.
Can anyone please explain?
JSP
<%# taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%# taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%# taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%# taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"></meta>
<link rel="stylesheet" href="<c:url value="/styles/resume.css"/>" type="text/css"></link>
<link href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/themes/base/jquery-ui.css" rel="stylesheet" type="text/css"></link>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8/jquery.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/jquery-ui.min.js"></script>
<script src="/resume/js/jquery.editable-1.0.1.js"></script>
<title>Resumes manager</title>
<script>
$(document).ready(function() {
$('.trash').click(function() {
$(this.parentNode).remove();
});
});
</script>
</head>
<body>
<h1>Personal data</h1>
<form:form modelAttribute="person" action="/resume/person/edit/save" id="personForm" method="post" >
<table>
<tr>
<td>Email addresses:</td>
<td colspan="4">
<ol id="emails">
<c:forEach items="${person.emails}" varStatus="status">
<li><form:hidden path="emails[${status.index}].order" class="emailsDisplayOrder"></form:hidden><form:input path="emails[${status.index}].label"></form:input><form:input type="email" path="emails[${status.index}].value"></form:input><input type="image" src="/resume/images/trash.png" class="trash" value="${status.index}"></input></li>
</c:forEach>
</ol>
</td>
</tr>
</table>
</form:form>
</body>
</html>
Controller
#Controller
#SessionAttributes(types={Person.class}, value={"person"})
public class PersonController {
private final static String PERSON_VIEW_NAME = "person-form";
private ResumeManager resumeManager;
#Autowired()
public PersonController(ResumeManager resume) {
this.resumeManager = resume;
}
#InitBinder
public void initBinder(WebDataBinder dataBinder) {
dataBinder.setDisallowedFields("id");
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
dateFormat.setLenient(false);
dataBinder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
}
#RequestMapping(value="/person/edit/save")
public String save(#ModelAttribute(value="person") Person p, BindingResult result, SessionStatus status) {
new PersonValidator().validate(p, result);
Collections.sort(p.getEmails()); //this collection still contains client-side dropped objects
this.resumeManager.savePerson(p);
return PERSON_VIEW_NAME;
}
}
Explanation
When you load a page with <form:form modelAttribute="person" ...>, there are two cases :
case 1 : if person doesn't exist, it creates an empty Person
case 2 : if person already exists, it uses it
In all cases, when a page is loaded, there is an existing person.
When you submit a form, Spring MVC updates this existing person only with the submitted information.
So in case 1, if you submit email 1, 2, 3 and 4, Spring MVC will add 4 emails to the empty person. No problem for you in this case.
But in case 2 (for example when you edit an existing person in session), if you submit email 1 and 2, but person has already 4 emails, then Spring MVC will just replace email 1 and 2. Email 3 and 4 still exist.
A possible solution
Probably not the best one, but it should work.
Add a remove boolean to the Email class :
...
public class Email {
...
private boolean remove; // Set this flag to true to indicate that
// you want to remove the person.
...
}
In the save method of your controller, remove the emails that have remove set to true.
Finally, in your JSP, add this hidden field :
<form:hidden path="emails[${status.index}].remove" />
And tell your Javascript to set the input value to true when the user clicks to delete the email.
Alternate solution to Jerome Dalbert one
Jerome's solution should work (thanks again for clear answer and solution), but I didn't want to modify business model.
So here is the way I found out: mark HTML elements to remove using java-script and actually remove it using ajax calls at form submit (I initially avoided ajax to keep model unchanged until user submits, but this solutions preserves that requirement).
JSP:
<%# taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%# taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%# taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%# taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"></meta>
<link rel="stylesheet" href="<c:url value="/styles/resume.css"/>" type="text/css"></link>
<link href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/themes/base/jquery-ui.css" rel="stylesheet" type="text/css"></link>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8/jquery.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/jquery-ui.min.js"></script>
<script src="/resume/js/jquery.editable-1.0.1.js"></script>
<title>Resumes manager</title>
<script>
$('.sortable').sortable({
update: function(event,ui) {
var liElements = this.getElementsByTagName('li');
$(liElements).each(function(i, liElement) {
var orderElements = liElement.getElementsByClassName('order');
$(orderElements).each(function (j, orderElement) {
orderElement.value = i;
});
});
}
});
$('.trashable').click(function() {
$(this.parentNode.childNodes).each(function(index, element) {
if(element.src.match(/trash.png/) != null) {
element.src = '/resume/images/back.png';
this.parentNode.className = 'trashed';
} else if(element.src.match(/back.png/) != null) {
element.src = '/resume/images/trash.png';
this.parentNode.className = '';
} else {
element.disabled = !element.disabled;
}
});
});
function trash(element) {
var sfx = element.alt;
var lnk = ('/resume/person/edit/').concat(sfx);
$.ajax({
url: lnk
});
}
$('#personForm').submit(function() {
var trashed = $(this).find('.trashed');
$(trashed).each(function(index, element) {
var img = $(element).find('.trashable');
var tmp = $(img)[0];
trash(tmp);
});
});
});
</script>
</head>
<body>
<h1>Personal data</h1>
<form:form modelAttribute="person" action="/resume/person/edit/save" id="personForm" method="post" >
<table>
<tr>
<td>Email addresses:</td>
<td colspan="4">
<ol class="sortable">
<c:forEach items="${person.emails}" varStatus="status">
<li><form:hidden path="emails[${status.index}].order" class="order"></form:hidden><form:input path="emails[${status.index}].label"></form:input><form:input type="email" path="emails[${status.index}].value"></form:input><img src="/resume/images/trash.png" class="trashable" alt="dropEmail/${person.emails[status.index].id}"></img></li>
</c:forEach>
</ol>
</td>
</tr>
<tr><td colspan="5"><form:button name="save" value="${person.id}">${person.id == 0 ? 'save' : 'update'}</form:button></td></tr>
</table>
</form:form>
</body>
</html>
Controller:
#Controller
#SessionAttributes(types={Person.class}, value={"person"})
public class PersonController {
private final static String PERSON_VIEW_NAME = "person-form";
private ResumeManager resumeManager;
#Autowired()
public PersonController(ResumeManager resume) {
this.resumeManager = resume;
}
#InitBinder
public void initBinder(WebDataBinder dataBinder) {
dataBinder.setDisallowedFields("id");
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
dateFormat.setLenient(false);
dataBinder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
}
#RequestMapping(value="/person/edit/{id}")
public String edit(#PathVariable("id") long personId, Model model) {
Person p = this.resumeManager.getPersonById(personId);
if(p != null) {
model.addAttribute("person", p);
return PERSON_VIEW_NAME;
} else {
return "redirect:/";
}
}
#RequestMapping(value="/person/edit/save")
public String save(#ModelAttribute(value="person") Person p, BindingResult result, SessionStatus status) {
new PersonValidator().validate(p, result);
Collections.sort(p.getEmails());
this.resumeManager.savePerson(p);
return PERSON_VIEW_NAME;
}
#RequestMapping(value="/person/edit/dropEmail/{id}")
#ResponseBody
public void dropEmail(#ModelAttribute(value="person") Person p, #PathVariable("id") long id) {
int i = 0;
for(Email e : p.getEmails()) {
if(e.getId() == id) {
p.getEmails().remove(i);
break;
}
i++;
}
}
}

Categories