#inputText(
signupForm("email"), '_label -> "Email",
'_help -> "Enter a valid email address."
)
How would I write this in pure html?
I have no idea how i add the value to the signupForm, so that I can use it in my controller with bindfromRequest() (in html)
Edit:
I normally used this approach
final static Form<User> signupForm = form(User.class);
and the the binding process
Form<User> filledForm = signupForm.bindFromRequest();
and my rendered form looks like this:
<div class="control-group ">
<label class="control-label" for="email">Email</label>
<div class="controls">
<input type="text" id="email" name="email" value="" >
<span class="help-inline"></span>
</div>
</div>
And this worked for me I was just curious how to use pure html, so I could create my own little helpers.
Edit2:
public static Result blank() {
return ok(form.render(signupForm));
}
and in the template itself
#(signupForm: Form[User])
Edit 3:
I don't know if this helps but the helper looks like this. (for the inputtext)
I just have no idea what this means, scala looks really cryptic to me.
#(field: play.api.data.Field, args: (Symbol,Any)*)(implicit handler: FieldConstructor, lang: play.api.i18n.Lang)
#input(field, args:_*) { (id, name, value, htmlArgs) =>
<input type="text" id="#id" name="#name" value="#value" #toHtmlArgs(htmlArgs)>
}
Use your browser to check the source coderendered by Play and copy/paste the HTML into your template.
In general the most interesting for you is proper inserting value to the manually created field:
<input type="text" name="email" value='#signupForm.field("email").value' />
It's also important to set the proper name attribut otherwise filling Form in controller will fail.
Edit:
Of course in blank action your signupForm is empty so that's normal that there's no value, in next action let's name it saveBlank, you need to fill the form with request's data, validate and save. If there are errors in form you need to render form.scala.view again with data binded to the form from previous request:
public static Result saveBlank(){
signupForm = signupForm.bindFromRequest();
if (signupForm.hasErrors()){
return badRequest(form.render(signupForm));
}
User user = new User();
user = signupForm.get();
user.save();
return ok("user saved);
}
of course if you'll want to edit existing user, you have to prefill it with data from DB ie:
public static Result edit(Long id){
signupform = signupForm.fill(User.find.byId(id));
return ok(form.render(signupForm));
}
(note: I wrote it just from top of my head, so check pls if there are no typos or errors)
Finally you don't need to use Form object in every case, you can also use DynamicForm:
public static Result saySomething(){
DynamicForm df = form().bindFromRequest();
return("you entered :" + df.get("email"));
}
or even in one line:
public static Result sayShorter(){
return("you entered :" + form().bindFromRequest().get("email"));
}
Related
I am trying to make a simple web app, that will have 3 forms for data input, Submit button and Results form.
Requirements: When I enter a data in forms and push Submit button, these parameters should go to a controller and after that React should also parse data from json response that comes from the controller and show parsed data in the Results form.
I have the Spring Controller that takes 3 parameters and return json file as a response.
But I am new with React. I've tried to use different approaches, but stuck with what way exactly I need to do it in this case. So need a help to create a simple React part.
Controller part:
#GetMapping("/current/city")
public JSONObject getCurrentPollutionDataByCityStateCountry(
#RequestParam(value = "city") String city,
#RequestParam(value = "state") String state,
#RequestParam(value = "country") String country
) {
try {
return pollutionService.getCurrentPollutionDataByCityStateCountry(city, state, country);
} catch (Exception e) {
e.printStackTrace();
}
return new JSONObject();
}
Response example:
{"date":"Tue Dec 06 22:13:32 CET 2022","no2":"48.67","pm10":"9.51","pm2_5":"5.85"}
UPDATE
Here is my App.js part:
import React, {Component} from 'react'
import './App.css'
import axios from 'axios'
class App extends Component {
constructor(props) {
super(props);
this.state = {
city: 'New York',
province: 'New York',
country: 'US',
responseData: ''
};
this.handleInputChange = this.handleInputChange.bind(this);
}
handleInputChange(event) {
const target = event.target;
const value = target.value;
const name = target.name;
this.setState({
[name]: value
});
}
handleSubmit(event) {
axios.get('http://localhost:8080/pollution/current/city?' +
'city=' + this.state.city +
'&state='+ this.state.province +
'&country=' + this.state.country)
//not sure about setState here and what is going after that
.then(response => this.setState({responseData: response.data.date}))
//need to take all fields from response
//just alert message with returned response for now
alert('Response: ' + this.state.responseData);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
City:
<input name="city" type="text" value={this.state.city} onChange={this.handleInputChange}/>
</label>
<br/>
<label>
State:
<input name="state" type="text" value={this.state.province} onChange={this.handleInputChange}/>
</label>
<br/>
<label>
Country:
<input name="country" type="text" value={this.state.country} onChange={this.handleInputChange} />
</label>
<br/>
<input type="submit" value="Submit"/>
</form>
);
}
}
export default App
But I am not sure about my approach in general and particularly about parts where I left comments in App.js.
To be more precise with questions:
Should this approach work for my case or I need to implement some different logic?
If yes, how I can get all fields from response? I mean not only the first one (date) but no2, pm10, pm2_5 also. For now this logic can return only a first value from json.
How I can set these fields to show them in pop-up window (alert) or if question 2 will be solved, this thing also will be good in current form?
The only problem I think you need to solve is to set the data not data.date
use this:
this.setState({responseData: response.data}))
then access your data fields using
responseData.date
you can replace date with any other field.
do the same for alerts.
your code is working fine, furthermore, you can learn to use functional component instead of class, its much easier.
I'm creating a web shop in JSP for a school project.
On each product page, there's a button to add it to the cart.
Originally, there is only one item added to the cart when we press the "Add" button.
I send the productId to add to the controller by adding it as parameter in the url as you can see here :
<div class="product-page">
<div class="product">
<h1>${product.title}</h1>
<img src="${product.imageUrl}"/>
<div class="price"><p>${product.price}€</p></div>
</div>
<c:url var="addLineToCart" value="/product/addLineCart">
<c:param name="productId" value="${product.id}" />
</c:url>
<a id="addToCart" type="submit" href="${addLineToCart}"><spring:message code="addCart"/></a>
What I'd like to do, is to add an input field to specify the amount of items to add in the cart. I did it here :
<div class="product-page">
<div class="product">
<h1>${product.title}</h1>
<img src="${product.imageUrl}"/>
<div class="price"><p>${product.price}€</p></div>
</div>
<input type="number" name="quantity" value="1"/>
<c:url var="addLineToCart" value="/product/addLineCart">
<c:param name="productId" value="${product.id}" />
</c:url>
<a id="addToCart" type="submit" href="${addLineToCart}"><spring:message code="addCart"/></a>
My problem is that I don't know how to pass the value from the input field in the <c:param/> property in order to add it in the URL too.
Here is how my controller looks like assuming I get the quantity to add via the URL :
#Controller
#RequestMapping(value="/product")
#SessionAttributes({Constants.CURRENT_CART})
public class ProductController {
private ProductDAO productDAO;
#Autowired
public ProductController(ProductDAO productDAO){
this.productDAO = productDAO;
}
#ModelAttribute(Constants.CURRENT_CART)
public Cart cart()
{
return new Cart();
}
#RequestMapping (method = RequestMethod.GET)
public String home(ModelMap model, #RequestParam("product") int productId, #ModelAttribute(value=Constants.CURRENT_CART) Cart cart){
Product product = productDAO.getById(productId);
model.addAttribute("product", product);
model.addAttribute("title", "Produit");
model.addAttribute("cart", cart);
return "integrated:product";
}
#GetMapping("/addLineCart")
public String addLineCart(#ModelAttribute(value=Constants.CURRENT_CART) Cart cart, #RequestParam("productId") int productId, #RequestParam("quantity") int quantity, ProductService productService)
{
Product product = productService.searchProduct(productId,productDAO);
cart.addProduct(product, quantity);
return "redirect:/product?product=" + productId;
}
}
Thanks for your help.
The easiest way is to wrap the fields in a HTML <form> tag and submit the data to your controller. You should also replace GET with a POST since your browser might decide to cache the response to some combinations of productId and quantity and your application might exhibit some unwanted behavior as result of that (don't forget to change your #GetMapping in your controller with a #PostMapping also).
There is also the option of submiting this to the server with JavaScript as an Ajax request, or to change the value of the URL in your existing link to include the quantity when you click it and before making the request to the server, but using a form with a POST action is the easiest and cleanest solution.
Finally, <c:url> and <c:param> are server side tags. They get evaluated at the server to produce your final HTML that gets sent to the client browser. You can't get your input value from the browser into your <c:param> because you are running client code at this point, no longer server code.
I would like to know how to create forms that uses th:object for each object looped in a th:each. For example, I have the following code.
HTML
<th:block th:each="store: ${stores}">
<form th:object="${store}" th:action="#{/modify-store}">
<input th:field="*{idStorePk}"/>
<input th:field="*{name}"/>
<input th:field="*{phoneNumber}"/>
<button type="submit">Modify</button>
</form>
</th:block>
Controller
#RequestMapping(value = "/stores")
public String getIndex(Model model) {
model.addAttribute("stores", storeService.getAllStores());
return "store";
}
So, I would like to add a form for each object, but it seems that it is not possible and I get the following error.
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'store' available as request attribute
So, I decided to add a #ModelAttribute in my controller, but can't get to return the actual store.
#ModelAttribute("store")
public Store getStore(Store store) {
return store;
}
With this approach all my forms have null values. I also tried to add a #PathVariable, but can't see to bind it using th:object. Is there a solution for this?
So for anyone stuck at a similar problem. I find out a work around that might help you out. First, you can't use th:object, it simply won't cut it. Instead, do the following.
<th:block th:each="store: ${stores}">
<form class="store-form" th:action="#{/modify-store}">
<input th:name="idStorePk" th:value="${store.idStorePk}"/>
<input th:name="name" th:value="${store.name}"/>
<input th:name="phoneNumber" th:value="${store.phoneNumber}"/>
<button class="submit-button" type="submit">Modify</button>
</form>
</th:block>
Then just add something similar to the controller.
#PostMapping(value = "/modify-store")
#ResponseBody
public boolean deleteEntry(#ModelAttribute Store store) throws Exception {
// Your code here...
return true;
}
If you want to send it asynchronously then you will need to add some JS code in order for it to work. It should look something like the code below.
const forms = document.querySelectorAll('.store-form');
forms.forEach(form => {
form.addEventListener('submit', event => {
// Stop the normal form submit triggered by the submit button
event.preventDefault();
const formInputs = form.getElementsByTagName("input");
let formData = new FormData();
for (let input of formInputs) {
formData.append(input.name, input.value);
}
fetch(form.action,
{
method: form.method,
body: formData
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.log(error.message))
.finally(() => console.log("Done"));
});
You're sending stores in your controller in model-attribute and on your second controller where you're submitting your form you're using store that's the reason you're getting this error. So correct the spelling error on any one of your controller. Like this :-
#RequestMapping(value = "/stores")
public String getIndex(Model model) {
model.addAttribute("stores", storeService.getAllStores());
return "store";
}
And Your second controller where you're submitting your form will be like this -
#ModelAttribute("stores")
public Store getStore(Store store) {
return store;
}
I am still relatively new to springMVC, I currently have some code, that will take an input from a <form:input> on a jsp page, and create different lists depending on the input(providing I enter the correct String). I would like to transition this into a button instead, so I could have four buttons that will return a different String, for example "one", "two", "three" and "four". That way there is no typing needed from the user.
I see that there is a <form:button> available but I do not know how I could return the String value from this. Also I have looked into angularJS and seen that you can call a function onClick. But again, I don't know what the implementation would have to be to tie it into my Controller. I am just not really sure how I can implement this. Any help would be appreciated.
This is what I have being implemented at the moment :
<form:form commandName="input">
<label>Enter Value</label>
<form:input path="listType" class="inputbox" />
<br>
<input type="submit" class="button" value="Enter" />
</form:form>
This takes the input and stores it in an object :
#Controller
#SessionAttributes("input")
public class EventController {
#RequestMapping(value = "/event", method= RequestMethod.GET)
public String displayEvent (Model model) {
AccessInput userInput = new AccessInput();
model.addAttribute("input", userInput);
System.out.println("finished get method");
return "event";
}
#RequestMapping(value = "/event", method= RequestMethod.POST)
public String processEvent(#ModelAttribute("input")AccessInput userInput) {
System.out.println(userInput.getListType()); //just so I know what value it has
return "redirect:results.html";
}
This is the controller that creates my list based on the string that I pass through to the object
#RestController
#SessionAttributes("input")
public class ReportController {
#RequestMapping(value="/events")
public List<Appliance> getEvents(#ModelAttribute("input")AccessInput userInput) {
List<Appliance> events = new ArrayList<>();
events = ProcessChoice.ofList(userInput.getListType());
System.out.println(userInput.getListType());
return events;
}
}
Edit:
Just to note I have resolved this, I followed the example given by Vipin Dubey, I had to change my controller. I removed the POST method and added in a #RequestParam as a parameter, and redirected the buttons on the event.jsp to "results.html?input=one" then took this value and added it to my model to store it in the session.
#Controller
#SessionAttributes("URLparam")
public class ResultController {
#RequestMapping(value = "/results.html", method = RequestMethod.GET)
public String buttonSelect(Model model, #RequestParam("input")String input) {
model.addAttribute("URLparam", input);
System.out.println(input);
return "result";
}
}
You have two options :
1. Short and recommended way :
Use a link and style it as a button and you can directly call your controller
<a class="btn" href="/events?input=one">One</a>
<a class="btn" href="/events?input=two">two</a>
<a class="btn" href="/events?input=three">three</a>
<a class="btn" href="/events?input=four">four</a>
2. Use jQuery or JavaScript to submit the form based on clicked button using a hidden input field in your form
<!-- Buttons with classes -->
<div id="target">
<button class="one">One</button>
<button class="two">two</button>
<button class="three">three</button>
<button class="four">four</button>
</div>
<!-- Your form -->
<form:form commandName="input" id="myForm">
<input type="hidden" name="inputbox" id="inputbox" value=""/>
</form:form>
// You will have to do this for each of the button which is not a recommended way
$( ".one" ).click(function() {
$('input[name="inputbox"]').val("one");
//var a = $('input[name="inputbox"]').val();
//alert(a);
$( "#myForm" ).submit();
});
I have the following code for validation
#RequestMapping(value = "/itemValidation.json", method = RequestMethod.POST)
#ResponseBody
public ValidationResponse ajaxValidation(
#ModelAttribute(value = formName) #Valid Item item,
BindingResult result) {
ValidationResponse res = new ValidationResponse();
if (!result.hasErrors()) {
res.setStatus("SUCCESS");
} else {
res.setStatus("FAIL");
List<FieldError> allErrors = result.getFieldErrors();
List<ErrorMessage> errorMesages = new ArrayList<ErrorMessage>();
for (FieldError objectError : allErrors) {
errorMesages.add(new ErrorMessage(objectError.getField(),
objectError.getDefaultMessage()));
}
res.setErrorMessageList(errorMesages);
}
return res;
}
Upon validation there are three elements which do not satisfy the constraints as shown below:
The problem is on the JSP only the last two of the errors are shown. The error with fieldName : itemPK.name is not shown.
I use the below code to show the errors:
<span class="help-inline"><form:errors path="${name}" /></span>
My generated input elements in sequence:
<input id="itemPK.name_id" name="itemPK.name" type="text" value="">
<input id="price_id" name="price" type="number" value="">
<input id="point_id" name="point" type="number" value="">
Not sure what went wrong, hope anyone can shed some light on this.
If you said that last 2 field errors are displayed then you are probably have wrong name of the first field. Use
<form:input path="name" /><span class="help-inline"><form:errors path="name" /></span>
or use the path value itemPK.name that is reflect to your field name in the debug window.
I found out what the problem was . There's another layer where an ajax response method suppose to append all the error messages to the appropriate fields. The first error was never shown due to the jQuery selector unable to locate element with name itemPK.name, the fix was to change it to itemPK\\.name.