I have a form for a new staff to fill in. But now, I want to have a edit form which return the form that user has filled.
This is my controller:
public static Result updateForm(String id) {
Form<staffInfo> existingStaffForm = Form.form(staffInfo.class);
staffInfo existingStaff = staffInfo.find.byId(id);
return ok(
views.html.update.render(existingStaffForm.fill(existingStaff))
);
}
This is my scala.html:
#(existingStaffForm: Form[staffInfo])
#import helper._
#implicitField = #{ FieldConstructor(myFieldConstructorTemplate.f) }
#main("Employment application") {
#form(routes.Application.newForm(), 'class -> "form-horizontal") {
<div class = "row">
<div class="col-md-6">#inputText(existingStaffForm("fullName"), 'size->50,'_label -> "Full name:", '_help -> "*", '_showConstraints -> false)</div>
<div class="col-md-6">#select(
existingStaffForm("sex"),
options(Seq("Male", "Female")),
'_label -> "Sex:")</div>
</div>
}
}
When I run on my browser, it return error [RuntimeException: Cannot fill a form with a null value].
At the controller on line -> return ok.
UPDATE
I've found out the main problem! I have a 'Update' button on the display page for the user to click (if they want to update their info), which the button will be directed to edit form page (the code is above). Here is code for the 'Update' button:
<input type="button" class="btn btn-default" value="Update">
And my routes:
GET /staff/:id/update controllers.Application.updateForm(id:String)
I think, the problem is at the href tag. Can someone help how to put the link in the correct way?
Apparently existingStaff is null, debug it somehow. i.e.:
public static Result updateForm(String id) {
Form<staffInfo> existingStaffForm = Form.form(staffInfo.class);
staffInfo existingStaff = staffInfo.find.byId(id);
if (existingStaff==null) {
return badRequest("Oooops existingStaff not found in DB... ");
}
return ok(
views.html.update.render(existingStaffForm.fill(existingStaff))
);
}
BTW: By the convention class name of the model should start with upper case: StaffInfo
You should generate your a href using reverse routing.
#routes.WhateverYourControllerIsNamed.updateForm("id")
see http://www.playframework.com/documentation/2.1.1/ScalaRouting
Related
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'm trying to get a selected value in my controller but can't get it to work.
In my view I have a form:
#(infoObjectForm: Form[Infoobject],nav: String = "")
#form(routes.Admin.admin_createMapInstance()) {
<fieldset>
#select(
infoObjectForm("infoobjectId"),
options(Infoobject.all_values),
'id -> "infoobjects_field",
'_label -> "Infoobject", '_default -> "--Select Infoobject--",
'_showConstraints -> false, 'value -> infoObjectForm
)
</fieldset>
<div class="actions">
<input type="submit" value="Create this Map Instance" class="btn primary"> or
Cancel
</div>
}
This dropdown shows all my info objects. When I choose one and hit the submit button, I want to use the selected infoobject (and it's properties) in the controller.
Form<Infoobject> mapForm = form(Infoobject.class).bindFromRequest();
if(mapForm.hasErrors()) {
flash("error", "MapForm contains an error");
return badRequest(createMapForm.render(mapForm, ""));
}
Infoobject infoobject = mapForm.get();
String desig = infoobject.getDesignation();
String desc = infoobject.getDescription();
...
The mapForm has errors. Do you guys know why?
Infoobject model:
#Entity
public class Infoobject extends Model {
#Id
#SequenceGenerator(name="infoobject_seq", sequenceName="infoobject_id_seq", allocationSize=1000)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="infoobject_seq")
public Long infoobjectId;
#Required
public String designation;
#Required
public String description;
...getters/setters
I've tried using two #inputText fields in my view, so I could create a info object, that works. But now I wan't to create a map instance of an existing info object.
Appreciate your help!
EDIT: Printed the error in console, saying "error.required". Anyone? It seems that the form doesn't contain any Infoobject. Found this simular question/answer..so why does this not work?
This solution work, but I don't think this is the best way.
<form>
#select(
infoObjectForm("infoobjectId.id"),
options(Infoobject.all_values),
'id -> "infoobjects_field",
'_label -> "Infoobject", '_default -> "--Select Infoobject--",
'_showConstraints -> false
)
<div class="actions">
<input type="button" value="Create this Map Instance" class="btn primary" onclick="JavaScript:createMap()"> or
Cancel
</div>
</form>
<script>
function createMap() {
var ioId = document.getElementById('infoobjects_field').value;
if(ioId) {
var nextUrl = "/CreateMapInstance?ioId="+ioId;
window.location = nextUrl;
}
}
</script>
I'll do a GET and sends just the ID in the url instead of sending the whole infoobject in a POST.
In the #select tag put infoObjectForm("infoobjectId")
In the model class Infoobject add this:
static {
play.data.format.Formatters.register(Infoobject.class, new ClassFormatter());
}
public static class ClassFormatter extends SimpleFormatter<Infoobject> {
#Override
public Infoobject parse(String text, Locale locale) {
if (text == null || text.trim().length() == 0)
return null;
return Infoobject.find.byId(Long.parseLong(text.trim()));
}
#Override
public String print(Infoobject value, Locale locale) {
return (value == null || value.id == null ? "" : value.id.toString());
}
}
I want to use rest web service to update the database and play framework is already rest service
what am i doing is:
i open a page that is user info updation page and when user click on save button i want to save that values in database and rendering to web page that your information has been updated
my view part:
#(id: Long, userForm: Form[User])
#import helper._
#import helper.twitterBootstrap._
#main("EXTR Show"){
<div class="container-narrow">
<h1>Edit user info</h1>
#form(routes.update.upd(id)) {
<fieldset>
#if(userForm.hasGlobalErrors) {
<p class="error">
<span><b>#userForm.globalError.message</b></span>
</p>
}
#inputText(userForm("email"),'_label->"Enter email")
#inputText(userForm("name"), '_label -> "user name")
#inputText(userForm("password"), '_label -> "password")
</fieldset>
<div class="actions">
<input type="submit" value="Save user" class="btn primary">
</div>
</div>
}
}
controller part is:
public static Result upd(Long id) {
Form<User> u = Form.form(User.class).bindFromRequest();
if (u.hasErrors()) {
return badRequest(editu.render(id, u));
} else {
u.get().update(id);
User user = u.get();
return ok(info.render(user));
}
}
routes:
GET /update/:id controllers.signin.ed(id:Long)
POST /update/:id controllers.update.upd(id: Long)
here in router i am using post to update that value but according top rest this is not good.
when i am using put at place of post it is giving error.
is it necessary to create ajax to use put method and if not how to use put method?
#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"));
}