(Unnecessary) markup in a Wicket form - java

While generating a simplistic form page using Wicket (version 7.5.0), I'm getting extra markup which seems unnecessary (a hidden field placed into a <div> with inline CSS):
<!DOCTYPE html>
<html xmlns:wicket="http://wicket.apache.org">
<head>
<meta charset="utf-8" />
<title>Apache Wicket Quickstart</title>
<link href='http://fonts.googleapis.com/css?family=Yanone+Kaffeesatz:regular,bold' rel='stylesheet' type='text/css' />
<link rel="stylesheet" href="mystyle.css" type="text/css" media="screen" title="Stylesheet"/>
</head>
<body>
<form method="post" wicket:id="ItemForm" id="ItemForm1" action="./tf?1-1.IFormSubmitListener-ItemForm">
<div style="width:0px;height:0px;position:absolute;left:-100px;top:-100px;overflow:hidden">
<input type="hidden" name="ItemForm1_hf_0" id="ItemForm1_hf_0" />
</div>
<p>
<label for="name">
<span>Item name:</span>
</label>
<input type="text" name="p::name" wicket:id="name" value="">
</p>
<p>
<label for="price">
<span>Item price:</span>
</label>
<input type="text" name="price" wicket:id="price" value="0">
</p>
<section>
<input type="submit" value="Submit">
</section>
</form>
</body>
</html>
The relevant Java class is:
// Package name and imports omitted
public final class ItemFormPage extends WebPage {
#EJB(name = "ejb/item")
Item it;
public ItemFormPage() {
Form f = new Form("ItemForm") {
#Override
public void onSubmit() {
setResponsePage(new ItemDisplay());
}
};
f.setDefaultModel(new CompoundPropertyModel(it));
f.add(new TextField("name"));
f.add(new TextField("price"));
add(f);
}
}
I'm new to Wicket, as is probably evident from the code. Is there a way to avoid generating the aforementioned seemingly unnecessary markup? In other words, am I missing something or should I report a bug?

This hidden input is used to submit the form with anchor-based components like SubmitLink.
For example, you have a Form and you want to have two ways to submit it (with different 2 buttons):
Form<Void> form = new Form<Void>("form") {
#Override
protected void onSubmit() {
// central form onSubmit
}
};
SubmitLink submitter1 = new SubmitLink("submitter1") {
#Override
public void onSubmit() {
System.out.println("submitter 1 called");
}
};
form.add(submitter1);
SubmitLink submitter2 = new SubmitLink("submitter2") {
#Override
public void onSubmit() {
System.out.println("submitter 2 called");
}
};
form.add(submitter2);
When you click any of the two submitters, its name will be put to that input, and Wicket will find the correct SubmitLink component and call its onSubmit().

Related

Can't create post request in thymeleaf

I tried to create POST request to send my form to the Spring server, but I'm only getting this error:
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'form' available as request attribute
My controller content:
// Form post handler
#PostMapping("/")
public String home(#ModelAttribute Form form, BindingResult bindingResult, Model model) {
Gson g = new Gson();
Form form_new = g.fromJson(JavaWebApplication.runtimeJsonContent, Form.class);
model.addAttribute("form", form);
model.addAttribute("ip", form.IP);
model.addAttribute("gateway", form.Gateway);
model.addAttribute("port", form.Port);
model.addAttribute("IncomingConnections", form.IncomingConnections);
return "index";
}
Here is my Form model:
public class Form {
public String IP;
public String Gateway;
public int Port;
public boolean IncomingConnections;
public int QoSEnable = 0;
public Form(){}
public Form(String IP, String gateway, int port, boolean incomingConnections) {
this.IP = IP;
this.Gateway = gateway;
this.Port = port;
this.IncomingConnections = incomingConnections;
this.QoSEnable = 0; // Assert that 0 is default
ExportToJson(this);
}
public Form(String IP, String gateway, int port, boolean incomingConnections, int qosEnable) {
this.IP = IP;
this.Gateway = gateway;
this.Port = port;
this.IncomingConnections = incomingConnections;
this.QoSEnable = qosEnable;
ExportToJson(this);
}
}
And my index.html webpage bound on / :
<!DOCTYPE html>
<html xmlns:th="https://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" th:href="#{./styles/main.css}">
<title>Document</title>
</head>
<body>
<form method="post" action="#" th:action="#{/}" th:object="${form}">
<div id="container">
<img src="https://kable-swiatlowodowe.net.pl/wp-content/uploads/2017/08/mikrotik.png" width="20%" style="margin-bottom: 5%;">
<br>
<div id="input-el">
<div class="input-element"><input type="text" placeholder="IP" name="IP" th:value="${ip}" th:field="*{IP}" id="input-element-ip"></div>
<div class="input-element"><input type="text" placeholder="Gateway" th:value="${gateway}" th:field="*{Gateway}" name="Gateway" id="input-element-gateway"></div>
<div class="input-element"><input type="number" placeholder="Port" th:value="${port}" th:field="*{Port}" name="Port" id="input-element-port"></div>
</div>
<div id="checkbox-el">
Incoming connections:<br>
<label><input type="checkbox" th:checked="${IncomingConnections}" th:field="*{IncomingConnections}" name="conections" id="conections-el">Allow</label>
</div>
QoS Mode<br>
<label><input type="radio" name="qos-mode-el" id="input-radio-game-first" checked>Game First</label>
<label><input type="radio" name="qos-mode-el" id="input-radio-multimedia-first">Multimedia First</label>
<button type="submit">Save</button>
</div>
</form>
</body>
</html>
Sample Postman request:
Response for this request:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="./styles/main.css">
<title>Document</title>
</head>
<body>
<form method="post">
<div id="container">
<img src="https://kable-swiatlowodowe.net.pl/wp-content/uploads/2017/08/mikrotik.png" width="20%" style="margin-bottom: 5%;">
<br>
<div id="input-el">
<div class="input-element">
<input type="text" placeholder="IP" name="IP" value="" id="input-element-ip"></div>
<div class="input-element">
<input type="text" placeholder="Gateway" value=""name="Gateway" id="input-element-gateway"></div>
<div class="input-element">
<input type="number" placeholder="Port" value="0" name="Port" id="input-element-port"></div>
</div>
<div id="checkbox-el">
Incoming connections:<br>
<label><input type="checkbox" name="conections" id="conections-el">Allow</label>
</div>
QoS Mode<br>
<label><input type="radio" name="qos-mode-el" id="input-radio-game-first" checked>Game First</label>
<label><input type="radio" name="qos-mode-el" id="input-radio-multimedia-first">Multimedia First</label>
<button type="submit">Save</button>
</div>
</form>
</body>
</html>
Each value content is clear
Any idea how to make this binding work correctly?
you are not passing the form object to your thymeleaf template
Do this -
#GETMapping("/")
public String getHome(Model model){
model.addAttribute("form", new Form());
return "index";
}
This will return a Form object to your thymleaf and then it can process the data in your code --
<form method="post" action="#" th:action="#{/}" th:object="${form}">
// This will now receive form object
</form>

Why am I getting null object after submitting form in Spring?

I am new to Spring and yesterday I created a simple app. I type book's title, author and genre and it saves it to List<Book>. Book.java contains private fields (title, author, genre).
So creating books and saving them to list works fine. Also I can view all books I have added. So it works fine. But now I want to create and delete them. So I have BookService.java that can add and delete books.
BookService.java
private List<Book> allBooks = new ArrayList<>();
public List<Book> getAllBooks() {
return allBooks;
}
public void addBook(String title, String author, String genre) {
allBooks.add(new Book(title, author, genre));
}
public void deleteBook(Book book) {
allBooks.remove(book);
}
This is stuff in my controller to delete books
#GetMapping("/books/delete")
public String deleteBook(Model model) {
model.addAttribute("BookList", bookService.getAllBooks()); // Works fine
return "delete";
}
#PostMapping("/books/delete")
public String deletedBook(#ModelAttribute Book book, Model model) {
System.out.println(book.getTitle()); // null
bookService.deleteBook(book); // can't delete null so nothing happens to the list
model.addAttribute("deletedBook", book);
return "deletedBookResult";
}
delete.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>Title</title>
</head>
<body>
<h2>Delete page</h2>
<div th:each="bookObj : ${BookList}"> <!-- BookList - all books I add using submit form -->
<form action="#" th:action="#{/books/delete}" th:object="${bookObj}" method="post"> <!-- I SEND bookObj -->
<input type="submit" th:value="${bookObj.title}"/> <!-- WORKS. I GET BOOK'S NAME ON THIS BUTTON-->
</form>
</div>
</body>
</html>
I use th:each="bookObj : ${BookList}". So bookObj is every book I add. That's why I use th:object=${bookObj}. There is a form for each book I added later. And it display's it's title on the button. But when I press it, I get null to IDEA's console and on webpage. Why?
Thank you in advance.
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>Title</title>
</head>
<body>
<h2 th:text=" 'You have just deleted' + ${deletedBook.title}"></h2>
<!-- You have just deleted null -->
</body>
</html>
You are not sending anything in your form. Add some hidden fields:
<div th:each="bookObj : ${BookList}">
<form action="#" th:action="#{/books/delete}" method="post">
<input type="hidden" th:name="genre" th:value="${bookObj.genre}"/>
<input type="hidden" th:name="author" th:value="${bookObj.author}"/>
<input type="hidden" th:name="title" th:value="${bookObj.title}"/>
<input type="submit" th:value="${bookObj.title}"/>
</form>
</div>
Or if you don't want to expose your data in html, you can try to use some sort of session objects instead (Probably an overkill, but sometimes can be useful):
#GetMapping("/books/delete")
public String deleteBook(Model model, HttpSession session) {
session.setAttribute("BookList", new Book[]{
new Book("Title", "Tom","genre"),
new Book("Title 2", "Jerry","genre2")}
);
return "delete";
}
#PostMapping("/books/delete")
public String deletedBook(HttpSession session, Integer id, Model model) {
Book[] books = (Book[]) session.getAttribute("BookList");
Book book = books[id];
System.out.println(book);
model.addAttribute("deletedBook", book);
return "deletedBookResult";
}
And use it like:
<div th:each="bookObj, iter : ${session.BookList}">
<form action="#" th:action="#{/books/delete}" method="post">
<input type="hidden" th:name="id" th:value="${iter.index}"/>
<input type="submit" th:value="${bookObj.title}"/>
</form>
</div>

Use web service in background of HTML

I have a web service and a html page and i want to calculate two values and show on the third text field but this code shows me on the next page. Any one help me for my FYP.
My index.html
<!DOCTYPE html>
<html>
<head>
<title>TO supply a title</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<h1>JAX-RS</h1>
<form action="http://localhost:8080/ConnectingToNode/webapi/myresource?ID" method="post" target="_self">
<p>
Number1 : <input type="text" name="number1" />
</p>
<p>
Number2 : <input type="text" name="number2" />
</p>
<p>
Number3 : <input type="text" name="number3" />
</p>
<input type="submit" value="Add" />
<p>
Total : <input type="text" name="number3" />
</p>
</form>
</body>
</html>
My web service
#Path("myresource")
public class MyResource{
HashMap<String, String> map= new HashMap<String, String>();
#POST
#Produces(MediaType.TEXT_PLAIN)
public Response addNumber(
#FormParam("number1") int number1,
#FormParam("number2") int number2
) {
int total=number1+number2;
return Response.status(200).entity("Total : " + total).build();
}
}
You want perform an ajax call instead of submitting the form. So just build a javascript function e.g. using Jquery:
$.post("<your servlet here>", $("#y<ourFormIdHere>").serialize(),callback);
Where callback is a function like this:
function(data){
console.log("Data",data); // do what you want with response data
}
Off course you have to bind the $.post function to your form button.

How to create multi layout in Spring MVC

I know about Apache Tiles in Spring, it seem working same jsp:include, but it doesn't solve my problem:
I want a file with name is layout1.jsp, in this file, I will define a layout like:
<html>
<head>
<style href="style1.css" />
</head>
<body>
<div class="main">
<div class="left">
<ul>
<li>
<li>
<li>
</ul>
</div>
<div class="content">
<h1>${message }</h1>
</div>
<div class="footer">
<span>This is footer</span>
</div>
</div>
</body>
</html>
And a file is layout2.jsp:
<html>
<head>
<style href="style2.css" />
</head>
<body>
<div class="main">
<div class="left2">
<ul>
<li>
<li>
<li>
</ul>
</div>
<div class="content2">
<h1>${message }</h1>
</div>
<div class="footer2">
<span>This is footer</span>
</div>
</div>
</body>
</html>
When user chose layout name on combobox, controller will set layout dynamic before render layout.
How should I do?
For switching the css file an you could use the Spring Theme Resolver. And you can use the same mechanism to change some small parts of your html (like the div classes)
<%# taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<html>
<head>
<style href="<spring:theme code='styleSheet'/>" />
</head>
<body>
...
<div class="<spring:theme code='contentClass'/>">
<h1>${message }</h1>
</div>
...
</body>
</html>
(But I would recommend to use the same html with same classes and just switch the css).
Do not forget to setup the theme resolver!
#See Spring Reference: Chapter Using themes
I found a method to solve this problem:
My application separator to multi module, example "Admin", "Default", each module have a interceptor, example a module interceptor:
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
String servletPath = request.getServletPath();
if (!servletPath.equals("/") && servletPath.substring(servletPath.length() - 1).equals("/")) {
response.sendRedirect(request.getContextPath() + servletPath.replaceAll("\\/+$", ""));
return false;
}
return true;
}
In view I have a class InternalResourceViewResolver.java:
package view;
import helper.AppHelper;
import java.util.Locale;
import org.springframework.web.servlet.View;
public class InternalResourceViewResolver extends
org.springframework.web.servlet.view.InternalResourceViewResolver {
public View resolveViewName(String viewName, Locale locale)
throws Exception {
AppHelper.setPage("../" + AppHelper.getModule() + "/" + viewName + ".jsp");
return super.resolveViewName("layout/" + AppHelper.getLayout(), locale);
}
}
AppHelper class:
package helper;
public class AppHelper {
private static String module = "default";
private static String layout = "main";
private static String page = "index";
public static String getModule() {
return module;
}
public static void setModule(String module) {
AppHelper.module = module;
}
public static String getLayout() {
return layout;
}
public static void setLayout(String layout) {
AppHelper.layout = layout;
}
public static String getPage() {
return page;
}
public static void setPage(String page) {
AppHelper.page = page;
}
}
In other module, example for Admin:
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
System.out.println("ServletPath" + request.getServletPath());
AppHelper.setModule("admin");
AppHelper.setLayout("admin");
return true;
}
And my view page:
In layout:
<%# page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%#taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!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">
<link href="<c:url value="/resources/css/main.css" />" rel="stylesheet">
<title>Welcome to web</title>
</head>
<body>
<div class="wrapper">
<div class="main">
<div class="header"></div>
<div class="content">
<div class="content-left">
<jsp:include page="<%=helper.AppHelper.getPage() %>"></jsp:include>
</div>
<div class="content-right"></div>
</div>
<div class="footer"></div>
</div>
</div>
</body>
</html>
Placeholder page:
<%#taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<h1>${message }</h1>
<h2>${welcome }</h2>
<c:forEach items="${batches }" var="batch">
${batch.id } - ${batch.name }
</c:forEach>
Here directory struct:
- src\main\webapp\WEB-INF\pages
- admin
- index
- index.jsp
- setting.jsp
- product
- index.jsp
- detail.jsp
- other
...
- default
- index
- index.jsp
- contact.jsp
- product
...
- layout
admin.jsp
main.jsp
You could do this using Tiles too ...
use apache-tiles 2.2.1 or above
In application's tiles.xml where you put layout models do this
<definition name="masterTile" templateExpression="${requestScope.layout} >
<put-attribute name="title" value="" />
<put-attribute name="header" value="/view/header.jsp" />
<put-attribute name="menu" value="/view/menu"/>
<put-attribute name="body" value="" />
<put-attribute name="footer" value="/view/footer.jsp"/>
</definition>
For more on this refer here Tiles Expression Language Support

wicket highlight inputs on error

In my other question, I described a little my form: wicket 1.5 not found error message
Not I'm trying to do little thing: highlighting an input on error in AjaxSubmit. The code looks like this:
add( new AjaxSubmitLink( "save", this )
{
private static final long serialVersionUID = 1L;
#Override
protected void onSubmit( AjaxRequestTarget target, Form<?> form )
{
user.setCryptedPassword( CypherUtil.encodeMd5( getNewPassword() ) );
userManager.saveOrUpdate( user );
// close popup
modalWindow.close( target );
}
#Override
protected void onError( AjaxRequestTarget target, Form<?> form )
{
/**
* Do podswietlania formow
*/
String jQueryString = "";
if( oldPassFeedbackPanel.anyErrorMessage() )
{
jQueryString += "jQuery('mp-oldpass').css({ 'background' : '#E41D1D' });";
}
if( newPassFeedbackPanel.anyErrorMessage() )
{
jQueryString += "jQuery('mp-newpass').css({ 'background' : '#E41D1D' });";
}
if( newPassRepeatFeedbackPanel.anyErrorMessage() )
{
jQueryString += "jQuery('mp-newpassrepeat').css({ 'background' : '#E41D1D' });";
}
target.appendJavaScript( jQueryString );
target.add( oldPassFeedbackPanel );
target.add( newPassFeedbackPanel );
target.add( newPassRepeatFeedbackPanel );
}
} );
It returns error message for the form, but even though the javascript is appended (Wicket Ajax Debug console shows it it appended) the background of inputs is not changed, any idea why? The HTML part of the code looks like this:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.4-strict.dtd">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body class="stats-popup-body">
<div class="stats-popup" id="car-info-edit-popup">
<p class="popup_title"> Edytuj Profilu </p>
<form wicket:id="profileModifyForm" class="stats-popup-form">
<div>
<label class="popup_field_label">Stare hasło:</label>
<input id="mp-oldpass" type="password" wicket:id="mp-oldpass" />
<span class="old_pass_error" wicket:id="mp-oldpass-error" />
</div>
<div class="clear9"></div>
<div>
<label class="popup_field_label">Nowe hasło:</label>
<input id="mp-newpass" type="password" wicket:id="mp-newpass" />
<span class="new_pass_error" wicket:id="mp-newpass-error" />
</div>
<div class="clear9"></div>
<div>
<label class="popup_field_label">Powtórz nowe hasło:</label>
<input id="mp-newpassrepeat" type="password" wicket:id="mp-newpassrepeat" />
<span class="new_pass_repeat_error" wicket:id="mp-newpassrepeat-error" />
</div>
<div class="clear9"></div>
<div class="clear1"></div>
<div class="button-box-bottom">
<input class="btn btn_save" style="margin-right: 9px;"
wicket:id="save" type="button" value="Zatwierdź"
onmousemove="this.className='btn btn_save btn_hover'"
onmouseout="this.className='btn btn_save'" />
<input
class="btn btn_cancel" wicket:id="cancel"
value="Anuluj" type="button"
onmousemove="this.className='btn btn_cancel btn_hover'"
onmouseout="this.className='btn btn_cancel'" />
</div>
<div class="stats-popup-close-x" wicket:id="close-x"></div>
</form>
</div>
</body>
</html>

Categories

Resources