Newly started using thymeleaf for my application. I am building a dynamic form from controller where form fields are created as a list of map. Each key value pair is iterated as below.
<form th:action="#" th:object="${foo}" method="post">...
<input type="text" id=${element.key}
th:name=${element.key} th:value="${element.value}"/> ...</form>
My Pojo has properties in lower case as following java standard. But when building the key property my code uses the Capital letters from external file and also in order to have a better readability on UI not converting from upper case to lower case conversion in
mydata.put("KEY", 90);.
Upon submitting the page, my form elements not binding with propery key of Foo class due to KEY to key is not mapping. Getting null for the form fields due to input form field name as 'KEY'. I suspect the reason for getting null values for input text fields entered in html pages are due to case-sensitive of object properties in Foo class.
class Foo { private String key; //KEY can be a fix but naming convention is missing. }
Is there any thymeleaf tags available to ignore cases when binding html-elements to objects ? Or shall I use any JavaScript tweak upon submitting the form ? Like sending lower case and converting to upper case on form load to display ?
you can try to use ${#strings.toLowerCase(element.key)}
<form th:action="#" th:object="${foo}" method="post">...
<input type="text" th:id="${#strings.toLowerCase(element.key)}" th:name=${#strings.toLowerCase(element.key)} th:value="${element.value}"/> ...
</form>
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.
What are the possible reasons for document.getElementById, $("#id") or any other DOM method / jQuery selector not finding the elements?
Example problems include:
jQuery silently failing to bind an event handler
jQuery "getter" methods (.val(), .html(), .text()) returning undefined
A standard DOM method returning null resulting in any of several errors:
Uncaught TypeError: Cannot set property '...' of null
Uncaught TypeError: Cannot set properties of null (setting '...')
Uncaught TypeError: Cannot read property '...' of null
Uncaught TypeError: Cannot read properties of null (reading '...')
The most common forms are:
Uncaught TypeError: Cannot set property 'onclick' of null
Uncaught TypeError: Cannot read property 'addEventListener' of null
Uncaught TypeError: Cannot read property 'style' of null
The element you were trying to find wasn’t in the DOM when your script ran.
The position of your DOM-reliant script can have a profound effect on its behavior. Browsers parse HTML documents from top to bottom. Elements are added to the DOM and scripts are (generally) executed as they're encountered. This means that order matters. Typically, scripts can't find elements that appear later in the markup because those elements have yet to be added to the DOM.
Consider the following markup; script #1 fails to find the <div> while script #2 succeeds:
<script>
console.log("script #1:", document.getElementById("test")); // null
</script>
<div id="test">test div</div>
<script>
console.log("script #2:", document.getElementById("test")); // <div id="test" ...
</script>
So, what should you do? You've got a few options:
Option 1: Move your script
Given what we've seen in the example above, an intuitive solution might be to simply move your script down the markup, past the elements you'd like to access. In fact, for a long time, placing scripts at the bottom of the page was considered a best practice for a variety of reasons. Organized in this fashion, the rest of the document would be parsed before executing your script:
<body>
<button id="test">click me</button>
<script>
document.getElementById("test").addEventListener("click", function() {
console.log("clicked:", this);
});
</script>
</body><!-- closing body tag -->
While this makes sense and is a solid option for legacy browsers, it's limited and there are more flexible, modern approaches available.
Option 2: The defer attribute
While we did say that scripts are, "(generally) executed as they're encountered," modern browsers allow you to specify a different behavior. If you're linking an external script, you can make use of the defer attribute.
[defer, a Boolean attribute,] is set to indicate to a browser that the script is meant to be executed after the document has been parsed, but before firing DOMContentLoaded.
This means that you can place a script tagged with defer anywhere, even the <head>, and it should have access to the fully realized DOM.
<script src="https://gh-canon.github.io/misc-demos/log-test-click.js" defer></script>
<button id="test">click me</button>
Just keep in mind...
defer can only be used for external scripts, i.e.: those having a src attribute.
be aware of browser support, i.e.: buggy implementation in IE < 10
Option 3: Modules
Depending upon your requirements, you may be able to utilize JavaScript modules. Among other important distinctions from standard scripts (noted here), modules are deferred automatically and are not limited to external sources.
Set your script's type to module, e.g.:
<script type="module">
document.getElementById("test").addEventListener("click", function(e) {
console.log("clicked: ", this);
});
</script>
<button id="test">click me</button>
Option 4: Defer with event handling
Add a listener to an event that fires after your document has been parsed.
DOMContentLoaded event
DOMContentLoaded fires after the DOM has been completely constructed from the initial parse, without waiting for things like stylesheets or images to load.
<script>
document.addEventListener("DOMContentLoaded", function(e){
document.getElementById("test").addEventListener("click", function(e) {
console.log("clicked:", this);
});
});
</script>
<button id="test">click me</button>
Window: load event
The load event fires after DOMContentLoaded and additional resources like stylesheets and images have been loaded. For that reason, it fires later than desired for our purposes. Still, if you're considering older browsers like IE8, the support is nearly universal. Granted, you may want a polyfill for addEventListener().
<script>
window.addEventListener("load", function(e){
document.getElementById("test").addEventListener("click", function(e) {
console.log("clicked:", this);
});
});
</script>
<button id="test">click me</button>
jQuery's ready()
DOMContentLoaded and window:load each have their caveats. jQuery's ready() delivers a hybrid solution, using DOMContentLoaded when possible, failing over to window:load when necessary, and firing its callback immediately if the DOM is already complete.
You can pass your ready handler directly to jQuery as $(handler), e.g.:
<script src="https://code.jquery.com/jquery-3.6.0.js" integrity="sha256-H+K7U5CnXl1h5ywQfKtSj8PCmoN9aaq30gDh27Xc0jk=" crossorigin="anonymous"></script>
<script>
$(function() {
$("#test").click(function() {
console.log("clicked:", this);
});
});
</script>
<button id="test">click me</button>
Option 5: Event Delegation
Delegate the event handling to an ancestor of the target element.
When an element raises an event (provided that it's a bubbling event and nothing stops its propagation), each parent in that element's ancestry, all the way up to window, receives the event as well. That allows us to attach a handler to an existing element and sample events as they bubble up from its descendants... even from descendants added after the handler was attached. All we have to do is check the event to see whether it was raised by the desired element and, if so, run our code.
Typically, this pattern is reserved for elements that don't exist at load time or to avoid attaching a large number of duplicate handlers. For efficiency, select the nearest reliable ancestor of the target element rather than attaching it to the document.
Native JavaScript
<div id="ancestor"><!-- nearest ancestor available to our script -->
<script>
document.getElementById("ancestor").addEventListener("click", function(e) {
if (e.target.id === "descendant") {
console.log("clicked:", e.target);
}
});
</script>
<button id="descendant">click me</button>
</div>
jQuery's on()
jQuery makes this functionality available through on(). Given an event name, a selector for the desired descendant, and an event handler, it will resolve your delegated event handling and manage your this context:
<script src="https://code.jquery.com/jquery-3.6.0.js" integrity="sha256-H+K7U5CnXl1h5ywQfKtSj8PCmoN9aaq30gDh27Xc0jk=" crossorigin="anonymous"></script>
<div id="ancestor"><!-- nearest ancestor available to our script -->
<script>
$("#ancestor").on("click", "#descendant", function(e) {
console.log("clicked:", this);
});
</script>
<button id="descendant">click me</button>
</div>
Short and simple: Because the elements you are looking for do not exist in the document (yet).
For the remainder of this answer I will use getElementById for examples, but the same applies to getElementsByTagName, querySelector, and any other DOM method that selects elements.
Possible Reasons
There are three reasons why an element might not exist:
An element with the passed ID really does not exist in the document. You should double check that the ID you pass to getElementById really matches an ID of an existing element in the (generated) HTML and that you have not misspelled the ID (IDs are case-sensitive!).
If you're using getElementById, be sure you're only giving the ID of the element (e.g., document.getElemntById("the-id")). If you're using a method that accepts a CSS selector (like querySelector), be sure you're including the # before the ID to indicate you're looking for an ID (e.g., document.querySelector("#the-id")). You must not use the # with getElementById, and must use it with querySelector and similar. Also note that if the ID has characters in it that aren't valid in CSS identifiers (such as a .; id attributes containing . characters are poor practice, but valid), you have to escape those when using querySelector (document.querySelector("#the\\.id"))) but not when using getElementById (document.getElementById("the.id")).
The element does not exist at the moment you call getElementById.
The element isn't in the document you're querying even though you can see it on the page, because it's in an iframe (which is its own document). Elements in iframes aren't searched when you search the document that contains them.
If the problem is reason 3 (it's in an iframe or similar), you need to look through the document in the iframe, not the parent document, perhaps by getting the iframe element and using its contentDocument property to access its document (same-origin only). The rest of this answer addresses the first two reasons.
The second reason — it's not there yet — is quite common. Browsers parse and process the HTML from top to bottom. That means that any call to a DOM element which occurs before that DOM element appears in the HTML, will fail.
Consider the following example:
<script>
var element = document.getElementById('my_element');
</script>
<div id="my_element"></div>
The div appears after the script. At the moment the script is executed, the element does not exist yet and getElementById will return null.
jQuery
The same applies to all selectors with jQuery. jQuery won't find elements if you misspelled your selector or you are trying to select them before they actually exist.
An added twist is when jQuery is not found because you have loaded the script without protocol and are running from file system:
<script src="//somecdn.somewhere.com/jquery.min.js"></script>
this syntax is used to allow the script to load via HTTPS on a page with protocol https:// and to load the HTTP version on a page with protocol http://
It has the unfortunate side effect of attempting and failing to load file://somecdn.somewhere.com...
Solutions
Before you make a call to getElementById (or any DOM method for that matter), make sure the elements you want to access exist, i.e. the DOM is loaded.
This can be ensured by simply putting your JavaScript after the corresponding DOM element
<div id="my_element"></div>
<script>
var element = document.getElementById('my_element');
</script>
in which case you can also put the code just before the closing body tag (</body>) (all DOM elements will be available at the time the script is executed).
Other solutions include listening to the load [MDN] or DOMContentLoaded [MDN] events. In these cases it does not matter where in the document you place the JavaScript code, you just have to remember to put all DOM processing code in the event handlers.
Example:
window.onload = function() {
// process DOM elements here
};
// or
// does not work IE 8 and below
document.addEventListener('DOMContentLoaded', function() {
// process DOM elements here
});
Please see the articles at quirksmode.org for more information regarding event handling and browser differences.
jQuery
First make sure that jQuery is loaded properly. Use the browser's developer tools to find out whether the jQuery file was found and correct the URL if it wasn't (e.g. add the http: or https: scheme at the beginning, adjust the path, etc.)
Listening to the load/DOMContentLoaded events is exactly what jQuery is doing with .ready() [docs]. All your jQuery code that affects DOM element should be inside that event handler.
In fact, the jQuery tutorial explicitly states:
As almost everything we do when using jQuery reads or manipulates the document object model (DOM), we need to make sure that we start adding events etc. as soon as the DOM is ready.
To do this, we register a ready event for the document.
$(document).ready(function() {
// do stuff when DOM is ready
});
Alternatively you can also use the shorthand syntax:
$(function() {
// do stuff when DOM is ready
});
Both are equivalent.
Reasons why id based selectors don't work
The element/DOM with id specified doesn't exist yet.
The element exists, but it is not registered in DOM [in case of HTML nodes appended dynamically from Ajax responses].
More than one element with the same id is present which is causing a conflict.
Solutions
Try to access the element after its declaration or alternatively use stuff like $(document).ready();
For elements coming from Ajax responses, use the .bind() method of jQuery. Older versions of jQuery had .live() for the same.
Use tools [for example, webdeveloper plugin for browsers] to find duplicate ids and remove them.
If the element you are trying to access is inside an iframe and you try to access it outside the context of the iframe this will also cause it to fail.
If you want to get an element in an iframe you can find out how here.
As #FelixKling pointed out, the most likely scenario is that the nodes you are looking for do not exist (yet).
However, modern development practices can often manipulate document elements outside of the document tree either with DocumentFragments or simply detaching/reattaching current elements directly. Such techniques may be used as part of JavaScript templating or to avoid excessive repaint/reflow operations while the elements in question are being heavily altered.
Similarly, the new "Shadow DOM" functionality being rolled out across modern browsers allows elements to be part of the document, but not query-able by document.getElementById and all of its sibling methods (querySelector, etc.). This is done to encapsulate functionality and specifically hide it.
Again, though, it is most likely that the element you are looking for simply is not (yet) in the document, and you should do as Felix suggests. However, you should also be aware that that is increasingly not the only reason that an element might be unfindable (either temporarily or permanently).
If script execution order is not the issue, another possible cause of the problem is that the element is not being selected properly:
getElementById requires the passed string to be the ID verbatim, and nothing else. If you prefix the passed string with a #, and the ID does not start with a #, nothing will be selected:
<div id="foo"></div>
// Error, selected element will be null:
document.getElementById('#foo')
// Fix:
document.getElementById('foo')
Similarly, for getElementsByClassName, don't prefix the passed string with a .:
<div class="bar"></div>
// Error, selected element will be undefined:
document.getElementsByClassName('.bar')[0]
// Fix:
document.getElementsByClassName('bar')[0]
With querySelector, querySelectorAll, and jQuery, to match an element with a particular class name, put a . directly before the class. Similarly, to match an element with a particular ID, put a # directly before the ID:
<div class="baz"></div>
// Error, selected element will be null:
document.querySelector('baz')
$('baz')
// Fix:
document.querySelector('.baz')
$('.baz')
The rules here are, in most cases, identical to those for CSS selectors, and can be seen in detail here.
To match an element which has two or more attributes (like two class names, or a class name and a data- attribute), put the selectors for each attribute next to each other in the selector string, without a space separating them (because a space indicates the descendant selector). For example, to select:
<div class="foo bar"></div>
use the query string .foo.bar. To select
<div class="foo" data-bar="someData"></div>
use the query string .foo[data-bar="someData"]. To select the <span> below:
<div class="parent">
<span data-username="bob"></span>
</div>
use div.parent > span[data-username="bob"].
Capitalization and spelling does matter for all of the above. If the capitalization is different, or the spelling is different, the element will not be selected:
<div class="result"></div>
// Error, selected element will be null:
document.querySelector('.results')
$('.Result')
// Fix:
document.querySelector('.result')
$('.result')
You also need to make sure the methods have the proper capitalization and spelling. Use one of:
$(selector)
document.querySelector
document.querySelectorAll
document.getElementsByClassName
document.getElementsByTagName
document.getElementById
Any other spelling or capitalization will not work. For example, document.getElementByClassName will throw an error.
Make sure you pass a string to these selector methods. If you pass something that isn't a string to querySelector, getElementById, etc, it almost certainly won't work.
If the HTML attributes on elements you want to select are surrounded by quotes, they must be plain straight quotes (either single or double); curly quotes like ‘ or ” will not work if you're trying to select by ID, class, or attribute.
On my website I have a few checkboxes each of them contains id in value attribute. After submitting form I'd like to have a list containing ids of checked checkboxes to be passed to the controller. That's how I want to make new page comparing n products.
Controller can accept List<Long> or long[]. That's what I have for now:
HTML:
<form th:action="#{/comparison}" th:object="${productsComparison}" target="_blank" method="post">
<table>
<tr data-th-each="item, iter : ${items.item}">
<td>
<input type="checkbox" th:name="|productsComparison.ids[${iter.index}]|" th:value="${item.id}"/>
</td>
</tr>
</table>
I've added to my controller List<Long> wrapped in ProductComparison with appropriate getters and setters. After submitting form list is always null.
Controller:
#RequestMapping("/productsPage")
public String showProducts(Model model) {
ProductsComparison productsComparison = new ProductsComparison();
model.addAttribute("productsComparison", productsComparison);
}
#RequestMapping("/comparison")
public String compareProducts(#ModelAttribute ProductsComparison productsComparison) {
System.out.println("List: " + productComparison.getIds());
// Always shows null
return "comparison";
}
public class ProductsComparison {
private List<Long> ids;
// Getters & setters
}
The underscores __ are ThymeLeaf's syntax for a preprocessing expression. Or in layman's terms, the stuff inside of the underscores are processed before the rest of the expression.
This is important because your expression is using an array, and the ${iter.index} portion of it gives the array index.
If you are curious or if anyone else stumbles upon this. th:name is no different than the html attribute name. The ThymeLeaf engine will just shove a name attribute into the html entity or overwrite the old one. ThymeLeaf has a ton of properties like this. th:field is a totally different beast though. It is what tells ThymeLeaf to bind the input of your form to the back forming bean or your model attribute, th:object="${productsComparison}". So with the expression th:field="*{ids[__${iter.index}__]}", Thymeleaf is going to call productsComparison.getIds[0] and the corresponding setter.
Note about th:name if you are submitting with Javascript of any kind, you probably want to use this. Most likely you are serializing your form, and the JSON created by this method call will use the name property.
If you want to know more about ThymeLeaf preprocessing expressions, there is a bit in the documentation about it.
I've finally found solution. Instead of th:name I had to use th:field.
th:field="*{ids[__${iter.index}__]}"
Because it's field I didn't have to specify object productsComparison before ids. ids field is List<String>.
To be honest I have no idea what underscores do, I will be glad if someone could explain.
In my jsp on readyfunction i am setting value for some variables
$(document).ready(function() {
........////some code.......
var compCode=$('#companyCode').val();
........////some code.......
$("#countrySectorinMemInfo").val(countrySec);
........////some code.......
});
my controller is returning one value say ABC and i am accessing this value using ,
<input type="hidden" value="${ABC}" id="companyCode">
suppose , if my jsp has more than two fields having same id as "countrySectorinMemInfo"
then how " $("#countrySectorinMemInfo").val(countrySec);" will work......
can anybody explain me ...???
both vaues will get set or only last value will be set ...?????
Multiple elements with the same ID is illegal in HTML. The browser behaviour is unspecified. Give them a different ID. If you need a common selector, use name or class instead.
I agree with #BalusC
you should use class names instead of IDs. I think all of the JavaScript libraries have methods to get elements by class name.
The jQuery way:
// Get all elements with class 'myclass'
var nodes = $('.myclass');
The YUI 2.7.0 Selector way:
// Get all elements with class 'myclass'
var nodes = YAHOO.util.Selector.query('.myclass');
The YUI 2.7.0 getElementsByClass way:
// Get all elements with class 'myclass'
var nodes = YAHOO.util.Dom.getElementsByClassName('myclass');