Traditionally we have always used xml in the response which is parsed by a Javascript method to execute post processes. I came up with a new, simpler implementation which uses a hidden input set by a requestAttribute and executed in an ajax callback.
JSP:
<%
String jsPostProcess = (String)request.getAttribute("jsPostProcess");
if (jsPostProcess!=null && jsPostProcess.trim().length()>0){
%>
<input type="hidden" id="jsPostProcess" name="jsPostProcess"
value="<%= jsPostProcess %> "/>
<% } %>
AJAX CALLBACK:
var callback = {
success: function(response) {
var div = $(divId);
if (div){
div.innerHTML = response.responseText;
}
var jsPostProcess = $('jsPostProcess');
if (jsPostProcess)
eval(jsPostProcess.value);
},
failure: function(response) {
alert('Something went wrong!');
}
}
SERVLET CODE:
request.setAttribute("jsPostProcess", jsPostProcess);
It works beautifully, and it is so much simpler for adding js post processes to virtually any call no matter how simple or complex the functionality is. No need for customized js methods for parsing.
Curious if anyone could identify any potential problems with it (such as security issues?) or make any suggestions for other alternatives. We currently use Prototype and YUI 2 on the front-end.
First, there's no need for that unpleasant scriptlet code:
<c:if test='${not empty jsPostProcess}'>
<input type='hidden' id='jsPostProcess' name='jsPostProcess' value='${jsPostProcess}'>
</c:if>
Next thing is that I hope that somewhere before this point the "jsPostProcess" value has been scrubbed so that it doesn't break the markup (like, in case it includes quotes).
Just calling eval() on the value like that seems a little dangerous, though perhaps you know pretty well what it's going to be.
Finally I would offer the suggestion that as an alternative to that, if the "post process" code isn't too big you could send it back in a response header. Then you wouldn't have to drop any meaningless markup into your page.
Oh, also finally: you might want to make the <input> be disabled. Or, alternatively, you don't even have to use an input: you can use this trick:
<script id='jsPostProcess' type='text/plain'>
${jsPostProcess}
</script>
Because the "type" attribute is "text/plain" the browsers won't try to execute that code, and you can get the "text" of the <script> element whenever you want.
Related
I have a form in JSP. I have to populate it based on the request object (from the servlet). How do I use Java Script for accessing request object attributes or if you can suggest me any other better way to populate form dynamically?
You need to realize that Java/JSP is merely a HTML/CSS/JS code producer. So all you need to do is to just let JSP print the Java variable as if it is a JavaScript variable and that the generated HTML/JS code output is syntactically valid.
Provided that the Java variable is available in the EL scope by ${foo}, here are several examples how to print it:
<script>var foo = '${foo}';</script>
<script>someFunction('${foo}');</script>
<div onclick="someFunction('${foo}')">...</div>
Imagine that the Java variable has the value "bar", then JSP will ultimately generate this HTML which you can verify by rightclick, View Source in the webbrowser:
<script>var foo = 'bar';</script>
<script>someFunction('bar');</script>
<div onclick="someFunction('bar')">...</div>
Do note that those singlequotes are thus mandatory in order to represent a string typed variable in JS. If you have used var foo = ${foo}; instead, then it would print var foo = bar;, which may end up in "bar is undefined" errors in when you attempt to access it further down in JS code (you can see JS errors in JS console of browser's web developer toolset which you can open by pressing F12 in Chrome/FireFox23+/IE9+). Also note that if the variable represents a number or a boolean, which doesn't need to be quoted, then it will just work fine.
If the variable happens to originate from user-controlled input, then keep in mind to take into account XSS attack holes and JS escaping. Near the bottom of our EL wiki page you can find an example how to create a custom EL function which escapes a Java variable for safe usage in JS.
If the variable is a bit more complex, e.g. a Java bean, or a list thereof, or a map, then you can use one of the many available JSON libraries to convert the Java object to a JSON string. Here's an example assuming Gson.
String someObjectAsJson = new Gson().toJson(someObject);
Note that this way you don't need to print it as a quoted string anymore.
<script>var foo = ${someObjectAsJson};</script>
See also:
Our JSP wiki page - see the chapter "JavaScript".
How to escape JavaScript in JSP?
Call Servlet and invoke Java code from JavaScript along with parameters
How to use Servlets and Ajax?
If you're pre-populating the form fields based on parameters in the HTTP request, then why not simply do this on the server side in your JSP... rather than on the client side with JavaScript? In the JSP it would look vaguely like this:
<input type="text" name="myFormField1" value="<%= request.getParameter("value1"); %>"/>
On the client side, JavaScript doesn't really have the concept of a "request object". You pretty much have to parse the query string yourself manually to get at the CGI parameters. I suspect that isn't what you're actually wanting to do.
Passing JSON from JSP to Javascript.
I came here looking for this, #BalusC's answer helped to an extent but didn't solve the problem to the core. After digging deep into <script> tag, I came across this solution.
<script id="jsonData" type="application/json">${jsonFromJava}</script>
and in the JS:
var fetchedJson = JSON.parse(document.getElementById('jsonData').textContent);
In JSP file:
<head>
...
<%# page import="com.common.Constants" %>
...
</head>
<script type="text/javascript">
var constant = "<%=Constants.CONSTANT%>"
</script>
This constant variable will be then available to .js files that are declared after the above code.
Constants.java is a java file containing a static constant named CONSTANT.
The scenario that I had was, I needed one constant from a property file, so instead of constructing a property file for javascript, I did this.
In JSP page :
<c:set var="list_size" value="${list1.size() }"></c:set>
Access this value in Javascipt page using :
var list_size = parseInt($('#list_size').val());
I added javascript page in my project externally.
In an ideal world I would like to separate out Javascript to a completely different file and include it in the JSP page. But there are cases were I struggle to follow this rule simply because dynamically generated Javascript is so much easier to write !!
Couple of examples :
1) Locale specific error messages in alert boxes.
<%
Locale locale = ..//get current locale
%>
<script language="JavaScript">
function checkMessage() {
if(document.form.msg.value=='') {
alert(<%= *LocaleHelper.getMessage(locale,"please_provide_message")* %>); //get the locale specific message . mixing Javascript and JSP !!!
}
}
2) Initializing values .Sometimes you need to get values using JSP which will be used inside a javascript method
function computeExpiry () {
var creationDate= <%= creationDate =%>
var currentDate = document.form.date.value;
var jsCreationDate= converToDate(creationDate);
return currentDate>creationDate ;
}
3) Initializing config objects dynamically
var myConfig = {
modal:true,
resize:true,
<% if (lastPage) { %>
showPreviousButton :true,
showNextButton : false ,
showSubmitButton : true,
<%} else {%>
showPreviousButton :true,
showNextButton : true ,
showSubmitButton : false,
<%} %>
As you can imagine, without any kind of conventions , all our JSPs will be a unsightly mix of Javascript and JSP, hard to understand and maintain, with lots of non-reusable javascript code
I am not looking for a perfect solution. I know its easier to do this than try to maintain a pure Javascript and JSP separation. I am looking for suggestions to ease this process and hope lots of people have experience worth sharing.
Mixing JavaScript and JSP makes code harder to read, reuse, maintain and also degrades performance(such JS code cannot be externalized and compressed). Avoid that when possible.
One way is collecting all JSP related variables in one place and factoring out non-JSP codes into another pure/static JavaScript files. For example:
<script type='text/javascript' >
var app = { // global app "namespace" holds app-level variables
msg: "<%= *LocaleHelper.getMessage(locale,"please_provide_message")* %>";
creationDate: <%= creationDate =%>;
btnConfig: {
<% if (lastPage) { %>
showNextButton : false ,
showSubmitButton : true,
<%} else {%>
showNextButton : true ,
showSubmitButton : false,
<%} %>
}
};
</script>
// following JS has no JSP, you can externalize them into a separate JS file
// s.t. they can be compressed and cached.
function checkMessage() {
if(document.form.msg.value=='') {
alert(app.msg);
}
}
function computeExpiry () {
var creationDate= app.creationDate;
var currentDate = document.form.date.value;
var jsCreationDate= converToDate(creationDate);
return currentDate > creationDate;
}
var myConfig = _.extend({ // underscore library's extend
modal: true,
resize: true,
showPreviousButton: true,
}, app.btnConfig);
BTW, in practical application, I recommend module loader library like RequireJS instead of defining global variables primitively.
I try to avoid this but when I have to pass data from back-end to front-end I use a flat JavaScript Object which is created by a Hash in the backend. You can convert Hashes in any language to JSON strings. Place a <script> tag in your HTML page and dump that JSON string in it and assign it to a JavaScript variable.
For example:
<script>
var dataFromBackEnd = JSON.parse(<%= Hash.toJSON(); %>); // I'm just assuming JSP part
</script>
From this point JavaScript should take care about logic. If there is a condition that JS need to take action about it pass the boolean value of it to JS. Don't mix logic.
First you can do is avoing using scriptlets. Instead using it use JSP tags, as JSTL. It will allow you to format your code better.
I have a text box that allow users to key in their desired unit,
and i would have to validate if this unit does exist.
Example 1: KM/H (Correct)
Example 2: KA/H (Wrong)
I know that emails address can be validated using codes like C#, JavaScript, Etc.
Is there any ways that I can validate (Units of measurement) through any codes, web service etc ? using Javascript, JSON?
P.s I am working on a JSP page :)
JavaScript is fully capable of validation (though it should not be used to validate at the expense of server-side validation, for security reasons). You can validate either by simple string checking (as below, which seems suitable to your case) or, for more complex, pattern-based validation, regular expressions.
Assumed HTML:
<form id='unit_form'>
<input type='text' name='unit' />
<input type='submit' value='go' />
</form>
Then, when the button is clicked (using jQuery...)
JS
$(function() {
var valid_units = ['KM/H', 'KM']; //etc...
$('#unit_form').on('submit', function() {
var unit = $('input[name=unit]').val();
if ($.inArray(unit, valid_units) == -1) {
alert('bad unit!');
return false; //cancel form submission
}
});
});
JS Fiddle
GNU publishes a nice little program called Units. It neatly does what you want.
arrays.jsp:
//...
var x = <c:out value="${x}"/>
<c:if test="${empty doExternal}">
processExternalArrays();
</c:if>
//...
I want to minify/obfuscate JavaScript contained in a large JSP file in which numerous JSP/JSTL variables are mixed into the JavaScript code such as in the snippet above.
The code relies on variables populated using server-side logic and then passed to the client-side code, as above.
I'm already minifying my JS files using YUI compressor but I don't know what to do about the JavaScript code in my JSPs.
Is it possible to minify/obfuscate this code, given that it is dynamically created?
Probably the best solution for you would be use Granule JSP tag.
You can download it at
http://code.google.com/p/granule/
code sample is:
<g:compress>
<script type="text/javascript" src="common.js"/>
<script type="text/javascript" src="closure/goog/base.js"/>
<script>
goog.require('goog.dom');
goog.require('goog.date');
goog.require('goog.ui.DatePicker');
</script>
<script type="text/javascript">
var dp = new goog.ui.DatePicker();
dp.render(document.getElementById('datepicker'));
</script>
</g:compress>
...
Have you taken a look at htmlcompressor? In short it's a:
Java HTML/XML Compressor is a very
small, fast and easy to use library
that minifies given HTML or XML source
by removing extra whitespaces,
comments and other unneeded characters
without breaking the content
structure.
It's main function is so compress HTML and XML, but it also comes with JSP tags that can be used to compress inline JavaScript blocks by leveraging YUI Compressor. Check out the Google Code page, especially the Compressing selective content in JSP pages section.
I don't see other ways than fully delegating the job to pure JS with help of Ajaxical powers in combination with a Servlet which returns the desired information on an Ajax request (in flavor of JSON?).
E.g. in Servlet
Map<String, Object> data = new HashMap<String, Object>();
data.put("doExternal", doExternal);
data.put("x", x);
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
response.getWriter().write(new Gson().toJson(data)); // Gson is a Java-JSON converter.
and in JS (with little help of jQuery since it makes the Ajax works less verbose)
$.getJSON('servleturl', function(data) {
var x = data.x;
if (!data.doExternal) {
processExternalArrays();
}
});
This way you end up with clean JS without server-side specific clutter.
Ensure that your output is gzip encoded (apache mod_deflate). Minimizing the html/js first may make it a bit smaller, but not by much.
If you can't, or don't want to, move your JavaScript out of your HTML, one possibility would be to create a tag handler that wraps the content of your <script> tags:
<script type="text/javascript"><js:compress>
...
</js:compress></script>
The handler could probably extend SimpleTagSupport. You'd then have to investigate the Java APIs for compressors/minifiers, like YUI Compressor or dojo ShrinkSafe, and use them to process the tag body.
Edit: Sorry, I skimmed the other answers and it appears that Zack Mulgrew might be referencing a taglib that already does exactly what I'm suggesting...
Edit2: Yup, JavaScriptCompressorTag. Guess I'll have to up-vote his answer ;-)...
I want to iterate a HashMap in javascript using jstl. is it possible to do like this?
function checkSelection(group,tvalue){
alert(group);
alert(tvalue);
<c:forEach items="${configuredGroupMap}" var="groupMap">
alert("aa<c:out value="${groupMap.key}"/>");
<c:if test="${groupMap.key==group}">
alert("t<c:out value="${groupMap.key}"/>");
<c:if test="${groupMap.value==tvalue}">
alert("equal");
</c:if>
</c:if>
</c:forEach>
}
it's not going inside after
<c:if test="${groupMap.key==group}">
"to iterate a HashMap in javascript using jstl" - Not possible
JSTL is executed in server side by your servlet container for which Javascript is just a text which would be skipped whereas JavaScript is executed in client side where JSTL is unknown. After the server completes processing JSTL, the generated HTML(if any) from the JSTL along with other JavaScript/HTML will be rendered.
For example, if you have this,
<c:forEach var="myItem" items="${myCollection}">
alert('<c:out value="${myItem.id}">')
<c:if test="${myItem.id == 0}">
alert("zero");
</c:if>
</c:forEach>
If the ids of the beans in the collection are 0, 1, 2, the server renders the following to the client side by executing the above code,
alert('0')
alert('zero')
alert('1')
alert('2')
Now the browser will give you 4 alerts on loading the page (what if you have 10000 items, you will render 10000 alert statements to the browser). So the point is that you haven't iterated Java collection in JavaScript, you have simply generated a serious of Javascript statements in server iterating the collection using JSTL and you have provided those Javascript statements along with other html contents to browser.
It is not possible because JSP is executed first at the server side, then the JavaScript gets executed at the client side.
You can still use the c:forEach to loop through the ${configuredGroupMap}, but you cannot do the comparison across groupMap.key and group directly.
Instead, a solution in this case is to assign the server-side groupMap.key to a client-side variable in javascript first. Then use javascript for the if check, instead of c:if.
I've modified your example to below
function checkSelection(group,tvalue){
alert(group);
alert(tvalue);
<c:forEach items="${stringshm}" var="groupMap">
alert("<c:out value="${groupMap.key}"/>");
var groupKey = "<c:out value="${groupMap.key}"/>";
if (groupKey == group){
alert("<c:out value="${groupMap.key}"/>");
var groupValue = "<c:out value="${groupMap.value}"/>";
if (groupValue == tvalue){
alert("both are equal");
}
}
</c:forEach>
}
Marimuthu has nailed it down. JavaScript and JSP/JSTL doesn't run in sync as you'd expect from the order in the coding. Java/JSP processes the page from top to bottom first, then the webserver sends the HTML/CSS/JS result to the webbrowser and finally the webbrowser processes the page (not containing any line of Java/JSP!) from top to bottom.
The best solution is to let JSP/JSTL generate a JavaScript object variable which you can later access in the JS code.
var groupMap = {
<c:forEach items="${configuredGroupMap}" var="groupMap" varStatus="loop">
"${groupMap.key}": "${groupMap.value}"${!loop.last ? ',' : ''}
</c:forEach>
};
This will end up like the following in client side (rightclick page and View Source to be sure)
var groupMap = {
"key1": "value1",
"key2": "value2",
"key3": "value3"
};
Finally rewrite checkSelection() as follows:
function checkSelection(group, tvalue) {
if (groupMap[group] == tvalue) {
alert("equal");
}
}
See also:
Communication between Java/JSP/JSF and JavaScript
Essential JavaScript tutorial
JSON (JavaScript Object Notation) tutorial