The foreach tag in JSP and how to write the code in the Java controller - forms

I have one controller (RegisteredController.java) , and I want that the output of the controller is displayed in the JSP (its name is commment_form.jsp). So I use a forEach tag in the jsp to display a list of comments (the comments which the user has inserted about a given resource). For "resource" I usually mean an image. So there are a list of comments about an "image" and I want that all the comments are all displayed in the bottom page, when a comment is going to be inserted into the comment form. My question is how must be written the code into the controller in order to set the output for the jsp ? Should I use a #ModelAttribute , a put-attribute or something else ? Here is the code of the controller and of the jsp :
The comment_form.jsp is:
<form:form modelAttribute="comments">
<table class="commento">
<tr>
<th/>
<th>ID</th>
<th>Contenuto</th>
</tr>
<c:forEach items = "${comments}" var="comment">
<tr>
<td><c:out value="${comments.content}"></c:out></td>
<td><c:out value="${comments.content}</c:out></td>
</c:forEach>
</table>
</form:form>
The RegisteredController.java is:
#RequestMapping("/comment.do")
public String comment(#ModelAttribute Comment comment, BindingResult
bindingResult, Model model, Locale locale) {
User user=userService.getUserCurrent();
comment.setDatePubblication(SQLUtility.getCurrentDate());
comment.setIdUser(user.getId());
commentService.create(comment);
Object[] args = { comment.getId() };
String message = messageSource.getMessage("message.update", args,locale);
List<Comment> comments =
commentService.findAllCommentByResource(comment.getIdResource());
model.addAttribute("comments", comments);
model.addAttribute("id",comment.getIdResource());
model.addAttribute("message", message);
model.addAttribute("comment", comment);
return "redirect:/registered/comment_start.do";
}
Please any help ? I will appreciate . Thanks you.

In case of redirect pass additional data as redirect attributes.
To carry data across a redirect use RedirectAttributes#addFlashAttribute(key, value).
What Java doc says:
A RedirectAttributes model is empty when the method is called and is never used unless the method returns a redirect view name or a RedirectView.
After the redirect, flash attributes are automatically added to the model of the controller that serves the target URL.
Read more...
One extra note :
In JSP it should be ${comment.content} instead of ${comments.content}

Related

Difference between modelAttribute and commandName attributes in form tag in spring?

In Spring 3, I have seen two different attribute in form tag in jsp
<form:form method="post" modelAttribute="login">
in this the attribute modelAttribute is the name of the form object whose properties are used to populate the form. And I used it in posting a form and in controller I have used #ModelAttribute to capture value, calling validator, applying business logic. Everything is fine here. Now
<form:form method="post" commandName="login">
What is expected by this attribute, is it also a form object whose properties we are going to populate?
If you look at the source code of FormTag (4.3.x) which backs your <form> element, you'll notice this
/**
* Set the name of the form attribute in the model.
* <p>May be a runtime expression.
*/
public void setModelAttribute(String modelAttribute) {
this.modelAttribute = modelAttribute;
}
/**
* Get the name of the form attribute in the model.
*/
protected String getModelAttribute() {
return this.modelAttribute;
}
/**
* Set the name of the form attribute in the model.
* <p>May be a runtime expression.
* #see #setModelAttribute
*/
public void setCommandName(String commandName) {
this.modelAttribute = commandName;
}
/**
* Get the name of the form attribute in the model.
* #see #getModelAttribute
*/
protected String getCommandName() {
return this.modelAttribute;
}
They are both referring to the same field, thus having same effect.
But, as the field name indicates, modelAttribute should be preferred, as others have also pointed out.
OLD WAY = commandName
...
<spring:url value="/manage/add.do" var="action" />
<form:form action="${action}" commandName="employee">
<div>
<table>
....
NEW WAY = modelAttribute
..
<spring:url value="/manage/add.do" var="action" />
<form:form action="${action}" modelAttribute="employee">
<div>
<table>
..
I had the same question a while ago, I can't remember the exact differences but from research I ascertained that commandName was the old way of doing it and in new applications you should be using modelAttribute
commandName = name of a variable in the request scope or session scope that contains the information about this form,or this is model for this view. Tt should be a been.
In xml based config, we will use command class to pass an object between controller and views. Now in annotation we are using modelattribute.

The value "" is invalid

Why does the Edit screen show the value I'm editing but when I try to save, the Edit HTTPPOST has a null object?
Getting a an error I've tracked down but don't see the cause of. I have a class used for a drop list, so it only has AdvisoryTypeID and AdvisoryType. But now I'm creating screens for users to see all in a grid (Index), add new (Create), and change existing ones (Edit). The index screen reads from the DB no problem. So does Edit. Create doesn't need to.
But both Edit and Create give the same error on HTTPPOST. The error is "The Value 'whatever I typed in' is invalid". Debugging the code turned up why: the "advisoryTypes" variable is null for reasons I don't understand:
CONTROLLER (for Edit post)
[Authorize]
[HttpPost]
public ActionResult Edit(AdvisoryTypes advisoryType)
{
try
{
if (ModelState.IsValid) //never get past this because it's null above
etc....
}
I have compared this to another screen that does Edit and Create fine and they're identical (except field names, for example). I'm stumped.
MODEL
public class AdvisoryTypes
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int AdvisoryTypeID { get; set; }
[Display(Name = "Type")]
[Required]
public string AdvisoryType { get; set; }
}
CONTROLLER (for Edit Get)
[Authorize]
public ActionResult Edit(int id = 0)
{
AdvisoryTypes advisoryType = db.AdvisoryType.Find(id);
if (advisoryType == null)
{
return HttpNotFound();
}
return View(advisoryType);
}
VIEW
model TheNewSolution.Models.AdvisoryTypes
#{
ViewBag.Title = "Edit Advisory Type";
}
<h2>Edit Advisory Type</h2>
#*<br />*#
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Advisory</legend>
<table>
<tr>
<td>
#Html.HiddenFor(model => model.AdvisoryTypeID)
<div class="editor-label">
#Html.LabelFor(model => model.AdvisoryType)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.AdvisoryType)
#Html.ValidationMessageFor(model => model.AdvisoryType)
</div>
</td>
</tr>
</table>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
PART TWO
Now some odd background: this is the first model/controller/view I created since updating EF from 5 to 6 alpha3 (had to because I had POCO and edmx in same project and this solved that bug). I created the model manually. When I tried to create the Controller via the wizard, the wizard gave an error "Unable to retrieve metadata for ProjectName.Models.AdvisoryProviders'. Unable to cast obect of type 'System.Data.Entity.Core.Objects.ObjectContext' to type 'System.Data.Objects.ObjectContext'.".
The result was having to manually create the controller by copying/pasting another one and making changes, then creating my views the same way. I'm suspicious this is all related but maybe not. If this proves unrelated I'll remove this and post as another question.
the error from EF of "Unable to retrieve metadata for ProjectName.Models.AdvisoryProviders i have seen before, but this has only been because i was using MySQL, the only way i found around this kind of error and to make sure everything worked was to use http://visualstudiogallery.msdn.microsoft.com/72a60b14-1581-4b9b-89f2-846072eff19d to create models from the database, and then use http://blog.stevensanderson.com/2011/01/13/scaffold-your-aspnet-mvc-3-project-with-the-mvcscaffolding-package/ to create the controllers, with view, rather than the buggy version of create view for EF.
I posted about these problems a while back with EF5 and its a real pain, MVCScaffolding seems to handle that pain alot better than the built in TT templates with MVC 4
hope this helps
I am not sure why this resolved, but here's what I did. I needed to create the screens mentioned above (index with grid, create, edit) for three different things (types, providers, categories) in my app. I did the first, type, resulting in the above issues.
I decided to create the same for "providers" by copying and pasting the controllers and views from "type" screens, then changing the model and field names as needed, expecting the screen to have the same bugs. But the screens all worked. I did it again for "categories", which also worked. Then I deleted my failing Create and Edit screens for "type", and recreated them from the "providers" screens. And they worked.
I have no explanation.

Spring Mvc/Jpa-OneToMany : How to display a list of class associated to another one

I've got a class Module with a OneToMany binding with a class Sequence.
My aim is to show the list of Modules, and by clicking on one of them, display the associated list of Sequences
But it doesn't work, I have a HTTP 500 error.
Here there is my controller :
#RequestMapping(value="formation", method = RequestMethod.GET)
public ModelAndView allModules() {
List<Module> allModules = moduleService.findAll();
return new ModelAndView("formation", "modules", allModules);
}
#RequestMapping(value="sequences/{module}", method = RequestMethod.GET)
public String displaySequences(#PathVariable ("module") Module module, Model model) {
List<Sequence> allSequences = sequenceService.findByModule(module);
model.addAttribute("sequences", allSequences);
return "sequences";
}
and the jsp which show the list of modules to return the list of sequences
<c:forEach items="${modules}" var="module">
<ul>
<li>${module.titre}
<br/>
</li>
</ul>
</c:forEach>
So, where does my error come from?
It works when I do that:
#RequestMapping(value="/sequences/{moduleId}", method = RequestMethod.GET)
public String displaySequences(#PathVariable ("moduleId") Long moduleId, Model model) {
Module module = moduleService.findById(moduleId);
model.addAttribute("module", module);
return "sequences";
}
and I change the link with :
<a href="sequences/${module}">${module.titre}
but I'd like to understand my error.
The reason why you weren't able to display sequences is Spring doesn't know how to parse this
/cmap-web/sequences/com.almerys.jpa.tomcatspring.Module#12b0f0ae
into Module instance.
You can read on this in Spring docs here in the section's 16.3.2.2 URI Template Patterns last paragraph. I paste it here for convenience.
A #PathVariable argument can be of any simple type such as int, long, Date, etc. Spring automatically converts to the appropriate type or throws a TypeMismatchException if it fails to do so. You can also register support for parsing additional data types. See Section 16.3.3.14, “Method Parameters And Type Conversion” and Section 16.3.3.15, “Customizing WebDataBinder initialization”.

Clear JSF form input values after submitting

If there's a form, and has a textbox and a button, how do you erase the content of the textbox after you submit the form?
<h:inputText id="name" value="#{bean.name}" />
<h:commandButton id="submit" value="Add Name" action="#{bean.submit}" />
After I enter a value in the textbox and submit, the value still appears in the textbox. I need to clear the content of the textbox once its been submitted. How can I achieve this?
Introduction
There are several ways to achieve this. The naive way is to simply null out the fields in backing bean. The insane way is to grab JS/jQuery for the job which does that after submit or even during page load. Those ways only introduces unnecessary code and indicates a thinking/design problem. All you want is just starting with a fresh request/page/view/bean. Like as you would get with a GET request.
POST-Redirect-GET
The best way is thus to just send a redirect after submit. You probably already ever heard of it: POST-Redirect-GET. It gives you a fresh new GET request after a POST request (a form submit), exactly as you intended. This has the additional benefit that the previously submitted data isn't re-submitted when the enduser ignorantly presses F5 afterwards and ignores the browser warning.
There are several ways to perform PRG in JSF.
Just return to same view with faces-redirect=true query string. Assuming a /page.xhtml, you could do so in action method:
public String submit() {
// ...
return "/page.xhtml?faces-redirect=true";
}
If you're still fiddling around with navigation cases the JSF 1.x way, then it's a matter of adding <redirect/> to the navigation case in question. See also How to make redirect using navigation-rule.
To make it more reusable, you can obtain the view ID programmatically:
public String submit() {
// ...
UIViewRoot view = FacesContext.getCurrentInstance().getViewRoot();
return view.getViewId() + "?faces-redirect=true";
}
Either way, if you've view parameters which needs to be retained in the request URL as well, then append &includeViewParams=true to the outcome. See also Retaining GET request query string parameters on JSF form submit.
If you're making use of some URL rewriting solution which runs outside JSF context, then you'd best grab the current request URL (with query string) and use ExternalContext#redirect() to redirect to exactly that.
public void submit() throws IOException {
// ...
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
StringBuffer requestURL = ((HttpServletRequest) ec.getRequest()).getRequestURL();
String queryString = ((HttpServletRequest) ec.getRequest()).getQueryString();
ec.redirect((queryString == null) ? requestURL.toString() : requestURL.append('?').append(queryString).toString());
}
It's only a mess which should really be refactored to some utility class.
Request/View scoped bean
Note that this all works only nicely in combination with request or view scoped beans. If you've a session scoped bean tied to the form, then the bean wouldn't be recreated from scratch. You've then another problem which needs to be solved as well. Split it into a smaller session scoped one for the session scoped data and a view scoped one for the view scoped data. See also How to choose the right bean scope?
Faces Messages
If you've a faces message to be shown as result of successful action, then just make it a flash message. See also How to show faces message in the redirected page.
public String submit() {
// ...
FacesContext context = FacesContext.getCurrentInstance();
context.addMessage(clientId, message);
context.getExternalContext().getFlash().setKeepMessages(true);
return "/page.xhtml?faces-redirect=true";
}
Ajax
Only if you happen to have an ajax-only page on which a F5 would always trigger a fresh new GET request, then simply nulling out the model field(s) in action method shouldn't harm that much.
See also:
How to navigate in JSF? How to make URL reflect current page (and not previous one)
Pure Java/JSF implementation for double submit prevention
You can blank out the property of the managed bean that should not be repainted when you render the response. This can be done done using code similar to the snippet posted below:
private String name;
public String getName(){return name;}
public void setName(String name){this.name=name};
public String submit()
{
//do some processing
...
// blank out the value of the name property
name = null;
// send the user back to the same page.
return null;
}
The reason for the current behavior can be found in how the JSF runtime processes requests. All JSF requests to a view are processed in accordance with the JSF standard request-response lifecyle. In accordance with the lifecyle, the managed bean contents are updated with the value from request (i.e. the value of DataForm.Name is set) before the application event (DataForm.submit) is executed. When the page is rendered in the Render Response phase, the current value of the bean is used to render the view back to the user. Unless the value is changed in an application event, the value will always be one that is applied from the request.
You can clear the form from the Bean method that gets called when the form is submitted;`
private String name;
private String description;
private BigDecimal price;
/*----------Properties ------------*/
/*-----Getter and Setter Methods---*/
public void save()throws SQLException{
String sql = "INSERT INTO tableName(name,description,price) VALUES (?,?,?)";
Connection conn = ds.getConnection();
try {
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, getName());
pstmt.setString(2, getDescription());
pstmt.setBigDecimal(3, getPrice());
pstmt.executeUpdate();
} catch (SQLException e) {
e.getMessage();
e.toString();
}finally{
conn.close();
clear();
}
}//End Save Method
public void clear(){
setName(null);
setDescription(null);
setPrice(null);
}//end clear`
Notice that the clear() method is called from the save method after all the operations of the save method is complete. As an option you could perform the clearing only if the methods operation was successful...The method below is placed in the ProductController Class...
public String saveProduct(){
try {
product.save();
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
The method call from the view/jsp would look like the Following:
<h:commandButton value="Save" action="#{productController.saveProduct}"/>
You can do it with jQuery.
I had the similar problem. I needed to clear popup window form.
<rich:popupPanel id="newProjectDialog" autosized="true"
header="Create new project">
<h:form id="newProjectForm">
<h:panelGrid columns="2">
<h:outputText value="Project name:" />
<h:inputText id="newProjectDialogProjectName"
value="#{userMain.newProject.projectName}" required="true" />
<h:outputText value="Project description:" />
<h:inputText id="newProjectDialogProjectDescription"
value="#{userMain.newProject.projectDescription}" required="true" />
</h:panelGrid>
<a4j:commandButton id="newProjectDialogSubmit" value="Submit"
oncomplete="#{rich:component('newProjectDialog')}.hide(); return false;"
render="projects" action="#{userMain.addNewProject}" />
<a4j:commandButton id="newProjectDialogCancel" value="Cancel"
onclick="#{rich:component('newProjectDialog')}.hide(); return false;" />
</h:form>
</rich:popupPanel>
jQuery code:
$('#newProjectForm').children('input').on('click', function(){$('#newProjectForm').find('table').find('input').val('');});
I added a code snippet how to reset all values for the current ViewRoot recursively for JSF 2 here:
Reset all fields in form
This works for submitted forms showing validation errors as well as for newly entered values in a form.

Html.RenderPartial & Multiple controls with same id error

I have made myself a small user control for consuming a feed, the code for said user control looks like this
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<SyndicationFeed >" %>
<%foreach (var rss in ViewData.Model.Items)
{
Response.Write("<div id={0}><a href={1} target=\"_blank\" /> <strong>{2}</strong></div>",
rss.Links[0].Uri.OriginalString, rss.Title.Text, rss.Title.Text);
Response.Write("<div>" + rss.Summary.Text.Truncate(100) + "</div>")
}%>
The code for the RssController looks like this
public virtual ActionResult Index()
{
string feedUrl = #"http://wdfw.wa.gov/news/newsrss.php";
using (XmlReader reader = XmlReader.Create(feedUrl))
{
SyndicationFeed rss = SyndicationFeed.Load(reader);
return View(rss);
}
}
And I call it in Site.Master like so
<%Html.RenderPartial("Index", Model);%>
I've also tried
<%Html.RenderPartial("Index", ViewData.Model);%>
All resulting in this this error:
Multiple controls with the same ID
'ctl00' were found. Trace requires
that controls have unique IDs.
Can someone help me figure out where I'm going wrong please :)
Can you try to use Html.RenderAction("Index","Rss") instead Html.RederPartial? When Html.RenderAction is called by ViewEngine, all code in your Index action is executed and Model with rss data goes to Index view to be generated to markup. And why do you use Response.Write, in view you can put html tags and this markup will be inserted where you are calling Html.RenderAction. Hope it will be helpful for you, if not, sorry, at least I've tried ))
Best regards,
Dima.