I'm using Spring, but this question applies to all JSP-controller type designs.
The JSP page references data (using tags) which is populated by the corresponding controller. My question is, where is the appropriate place to perform formatting, in JSP or the controller?
So far I've been preparing the data by formatting it in my controller.
public class ViewPersonController extends org.springframework.web.servlet.mvc.AbstractController
{
private static final Format MY_DATE_FORMAT = new SimpleDateFormat(...);
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
{
Person person = get person from backing service layer or database
Map properties = new HashMap();
// No formatting required, name is a String
properties.put("name", person.getName());
// getBirthDate() returns Date and is formatted by a Format
properties.put("birthDate", MY_DATE_FORMAT.format(person.getBirthDate()));
// latitude and longitude are separate fields in Person, but in the UI it's one field
properties.put("location", person.getLatitude() + ", " + person.getLongitude());
return new ModelAndView("viewPerson", "person", properties);
}
}
The JSP file would look something like:
Name = <c:out value="${person. name}" /><br>
Birth Date = <c:out value="${person. birthDate}" /><br>
Location = <c:out value="${person. location}" /><br>
I know that JSP does have some provisions for formatting,
<%# taglib uri="http://java.sun.com/jstl/fmt" prefix="fmt" %>
<fmt:formatDate type="date" value="${person. birthDate}" />
But this only works with Java's java.util.Format. What if I need more complex or computed values. In such a case putting the code in the JSP would be cumbersome (and ugly).
I'm curious if this is following the spirit Spring/JSP/MVC. In other words, is the controller part of the view? Where is the preferred place to perform view related formatting? Should my controller just be returning the object (Person) instead of a Map of formatted values?
JSPs typically do not have a lot (or any?) code in them, so your options would be
controller
tag libraries
I would say that a tag library would probably be what you want for most cases, because typically the view is the code that cares about things like formatting.
If standard tag libraries don't get you there, they are not hard to create, so you can roll your own.
I typically do formatting, etc. in the bean or a view "helper". This has several advantages including the following:
Easier to test
Flexibility to change your view technologies without worrying about porting or rewriting what you've done in custom tablibs.
Cleaner and easier to maintain controller and view code.
I prefer to consider formatting part of the display layer, thus done in the JSP. I used Velocity most recently, but same idea with JSP: controller returns a data model, and the view is responsible for rendering that data into a visible representation. Plenty of JSP tag libraries out there for common needs.
You mention complex or computed values. Those sound like elements of the results data model to me, so should be done in the controller, even if they can in principle be determined by other data, such as sum, max and other aggregate values. By formatting in the view I mean basic things like date and number formats, line splitting, alignment. Of course the exact line between data and formatted representation depends on the application, but I think you get the idea.
The way I would do it is -
an instance of the Person class would be the only object in the Model of the ModelAndView
I would move the "presentation logic" into the Person class itself. For example,
public class Person {
public String getLocation() {
return this.latitude.concat(", ").concat(this.longitude);
}
}
I think overall this approach:
1 - strengthens your domain model.
2 - reduces code duplication (what if you wanted to show the location in another JSP? With your approach you'd have a lot of code duplicated)
Related
I am want to create simple form for searching records via one parameter (for example, name).
Seems like creating a class with one property (name) and than use helpers for forms - is not a best way.
Is there any examles how can I get POST data from request and fetch property value from that data?
Thanks a lot for wasting your time.
You already answered your own question, I just want to provide some more information:
You are right about creating a class with one single property, however keep in mind that you can use validation annotations (like #Required, #Email, etc.) in this class - so if there is some (super) complex logic behind this property this might also be a valuable option.
The second solution would be to use DynamicForms - you use them when you don't really have a model that is backing up the submission form. It goes like this:
public static Result index() {
DynamicForm requestData = Form.form().bindFromRequest();
String name = requestData.get("name");
return ok(name);
}
And of course the third option to get the values is like you mentioned:
String name = request().body().asFormUrlEncoded().get("name")[0];
If you do not what to use form validation, I don't think you need to create a class. Instead, you can use AJAX function like $.ajax(), that will be route to your specific controller function. Moreover, you can call your model function from your controller then at last return the result. The result will be caught by the $.ajax() function.
$.ajax
type: "POST"
url: url
data: data
success: success
dataType: dataType
I'm really new to JSTL and having trouble grasping exactly how for each loops work. But say in my java bean I have a very simple while loop, that goes through and grabs properties of an object. I get the expected output from the loop when I log it. Which is simply a string that looks something like headerTest, headerMetaTest. Here is the code from my java bean:
Iterator<Resource> serviceList = null;
serviceList = resource.getChild("header").listChildren();
while(serviceList.hasNext()){
Resource child = serviceList.next();
headerTitle = child.adaptTo(ValueMap.class).get("headerTitle", "");
headerMeta = child.adaptTo(ValueMap.class).get("headerMeta, "");
}
However when I try to access it in the JSTL I'm getting nothing:
<c:forEach var="child" items="${serviceList}">
<p>${child.headerTitle}</p>
<p>${child.headerMeta}</p>
</c:forEach>
The puzzling part is I get no errors, nothing simply returns. Any ideas? Really, really lost on this one and any help is greatly appreciated. I'm a newb to this so code samples are a good way for me to learn and would be great if possible.
There are four scopes to be aware of in JSP pages.
page, request, session and application.
JSTL tags will usually look for attributes in that order.
page maps to attributes assigned during the processing of the page, these are usually quite
rare.
request is for attributes assigned to the ServletRequest, they are the most common
attributes to use as they last for the page request duration, and are then discarded.
eg
public void processMyServlet(ServletRequest request, ServletResponse){
...
request.setAttribute("myAttribute",attributeValue);
...
}
session is for attributes assigned to the HttpSession. This is useful for
user values that are used often during the user session.
eg
public void processMyServlet(HttpServletRequest request, HttpServletResponse){
...
request.getSession().setAttribute("myAttribute",attributeValue);
...
}
application is for attributes assigned to the ServletContext, this is useful for
values that are consistent across the application and do not change.
eg
public void processMyServlet(HttpServletRequest request, HttpServletResponse){
...
request.getServletContext().setAttribute("myAttribute",attributeValue);
...
}
If you are calling a servlet that dispatches your jsp then at the very least you will need.
request.setAttribute("serviceList",myResourceCollection);
somewhere during the servlet processing.
if you are doing everything in jsp then you will need something like
<% java code to create collection
request.setAttribute("serviceList",myResourceCollection);
%>
I should probably point out that Spring is not in and of itself necessarily crucial to this question, but I encountered this behavior while using Spring, so the question uses the situation in Spring in which I encountered this.
I have a controller class that maps requests for GET and POST requests to the same set of URLs for a particular form. This form has different URLs for different locales, but there is only one method for the GET request, and one for the POST, since the logic at the controller level for the form is identical for each locale site (but things deeper in the logic, like locale-specific validation, may be different). Example:
#Controller
public class MyFormController {
// GET request
#RequestMapping(value={"/us-form.html", "/de-form.html", "/fr-form.html"},
method={RequestMethod.GET})
public String showMyForm() {
// Do some stuff like adding values to the model
return "my-form-view";
}
// POST request
#RequestMapping(value={"/us-form.html", "/de-form.html", "/fr-form.html"},
method={RequestMethod.POST})
public String submitMyForm() {
// Do stuff like validation and error marking in the model
return "my-form-view"; // Same as GET
}
}
The form GET and POST works just fine when written like this. You'll notice that the String arrays used for the #RequestMapping values are identical. What I want to do is put those URLs into one spot (ideally a static final field in the controller) so that when we add new URLs (which correspond to the form in future localized sites), we can just add them in one spot. So I tried this modification to the controller:
#Controller
public class MyFormController {
// Moved URLs up here, with references in #RequestMappings
private static final String[] MY_URLS =
{"/us-form.html", "/de-form.html", "/fr-form.html"};
// GET request
#RequestMapping(value=MY_URLS, // <-- considered non-constant
method={RequestMethod.GET})
public String showMyForm() {
// Do some stuff like adding values to the model
return "my-form-view";
}
// POST request
#RequestMapping(value=MY_URLS, // <-- considered non-constant
method={RequestMethod.POST})
public String submitMyForm() {
// Do stuff like validation and error marking in the model
return "my-form-view"; // Same as GET
}
}
The problem here is that the compiler complains about the value attribute no longer being a constant. I am aware that Spring requires that value must be a constant, but I had thought that using a final field (or static final in my case) with an Array literal containing String literals would have passed as "constant". My suspicion here is that the array literal has to be constructed on the fly in such a way that it is uninitialized when the value attribute is parsed.
I feel like this shouldn't be a hard thing to figure out with a basic Java knowledge, but something is escaping me that I haven't been able to find any answers for after some research. Can someone confirm my suspicion and give a citation or good explanation for why that may be so, or deny my suspicion and explain what the actual issue is?
Note: I cannot simply combine the URLs into a Path Pattern, as each form URL is in its localized site's language, and matching on that would be impossible. I merely give the "/{locale}-form.html" strings above as my URLs for example's sake.
You're right, this is nothing to do with Spring, all Annotation parameters must be compile-time constants. That's a basic java language rule.
Marking the array reference as final doesn't cut it because this is still perfectly legal:
MY_URLS[0] = "es-form.html";
Also, how locked in are you into embedding locale into the url like that in the first place? Are you emulating legacy links? Spring has plenty of built in support for using the browser's actual locale.
I am building an application with simple Servlets and the MVC pattern. I am not using any framework like Spring.
I have some Model classes like this:
public class BlogPost {
private List<Comment> _comments;
// Things
}
and
public class Comment {
// Code
}
Posts can have zero or more comments associated with them in that collection.
However, I want to attach some additional information to the BlogPost Model before it is passed to the View, namely a value I set in a Cookie once a user makes a comment on a BlogPost. Strictly speaking, this is not a part of the BlogPost Model itself -- it is unrelated, incidental information, however I am not sure if I should make it easy on myself and just add it to the BlogPost class or do something to abstract this out a bit more.
So, should I add a field to the BlogPost class to handle this additional value, OR should I make a "View Model" along the lines of this which gets passed to the JSP view:
public class BlogPostView {
public BlogPostView(BlogPost bp, String message) {
// Constructor stuff, save these to instance variables
}
public BlogPost getBlogPost() { /* ... */ }
public String getMessage() { /* ... */ }
}
If BlogPost and your cookie data are unrelated, it is a bad idea to put the cookie data in your BlogPost class. The BlogPost class should represent what it's called - a blog post. It would be confusing to have other data associated.
Your second option of creating a class specifically to pass to the view is a better idea, though I'm curious to know why you need to pass the blog post and the cookie data as one object to your view? If you're using raw servlets:
request.setAttribute("blogPost",blogPost);
request.setAttribute("cookieData",cookieData);
Using a model class (e.g. Spring MVC ModelMap):
model.addAttribute("blogPost",blogPost);
model.addAttribute("cookieData",cookieData);
Your view will have access to both pieces of data, which you can manipulate using JSTL or other tag libraries.
If there's something I'm missing, can you elaborate more?
Create a HashMap model - and pass it along with the response to view.
model.put("blog", blog)
model.put("message", "some message")
I have to warn you: the question may be rather silly, but I can't seem to wrap my head around it right now.
I have two managed beans, let's say A and B:
class A
{
private Date d8; // ...getters & setters
public String search()
{
// search by d8
}
}
class B
{
private Date d9; //...getters & setters
public String insert()
{
// insert a new item for date d9
}
}
and then I have two JSP pages, pageA.jsp (the search page) and pageB.jsp (the input page).
What I would like to do is placing a commandbutton in pageB so to open the search page pageA passing the parameter d9 somehow, or navigating to pageA directly after b.insert(). What I would like to do is showing the search result after the insertion.
Maybe it's just that I can't see the clear, simple solution, but I'd like to know what the best practice might be here, also...
I though of these possible solutions:
including **A** in **B** and linking the command button with **b.a.search**
passing **d9** as a **hiddenInput** and adding a new method **searchFromB** in **A** (ugly!)
collapsing the two beans into one
JSF 1.1/1.2 raw doesn't provide an easy way to do this. Seam/Spring both have ways around this and there are a couple of things you can do. JSF 2 should also have solutions to this once it is released.
Probably the easiest and most expedient would be to collapse the two beans into one and make it session scoped. The worry, of course, is that this bean will not get removed and stay in session until the session times out. Yay Memory leaks!
The other solution would be to pass the date on as a GET parameter. For instance, you action method could call the
FacesContext.getCurrentInstance().getExternalContext().redirect("pageB?d9=" + convertDateToLong(d9));
and then get the parameter on the other side.
You should configure the navigation flow in faces-config.xml. In ideal scenario you would return a "status" message which would decide the flow. Read more at following link:
http://www.horstmann.com/corejsf/faces-config.html
http://publib.boulder.ibm.com/infocenter/rtnlhelp/v6r0m0/index.jsp?topic=/com.businessobjects.integration.eclipse.doc.devtools/developer/JSF_Walkthrough8.html
As far as passing the values from one page to another is concerned you can use backing beans. More about backing beans here:
http://www.netbeans.org/kb/articles/jAstrologer-intro.html
http://www.coderanch.com/t/214065/JSF/java/backing-beans-vs-managed-beans
Hope i have understood and answered correctly to your question
Way to share values between beans
FacesContext facesContext = FacesContext.getCurrentInstance();
Application app = facesContext.getApplication();
ExpressionFactory elFactory = app.getExpressionFactory();
ELContext elContext = facesContext.getELContext();
ValueExpression valueExp = elFactory.createValueExpression(elContext, expression, Object.class);
return valueExp.getValue(elContext);
In above code "expression" would be something like #{xyzBean.beanProperty}
Since JSF uses singleton instances, you should be able to access the values from other beans. If you find more details on this technique, I am sure you'll get what you are looking for.
Add commandButton action attribute referencing to B'insert method
<h:commandLink action="#{b.insert}" value="insert"/>
In B'insert method,add d9 parameter as request parameter. Then return an arbitrary string from insert method.
FacesContext fc = FacesContext.getCurrentInstance();
fc.getExternalContext().getRequestMap().put("d9", d9);
Then go to faces context and add navigation from B to A with "from-outcome" as the arbitrary String you returned from insert method. But don't add redirect tag to navigation tags as it will destroy the request coming from B and the parameter you added (d9) will be cleared.
<from-outcome>return string of insert method</from-outcome>
<to-view-id>address of A</to-view-id>
Then you might get the "d9" in A class by fetching it from request map at its constructor or in a place where its more appropriate (getters). You might add it into a session scope or place it to a hidden variable if you want to keep track of it later.
in class A, when page is navigated, A should be initialized as it will be referenced.
FacesContext fc = FacesContext.getCurrentInstance();
fc.getExternalContext().getRequestMap().get("d9", d9);
Sorry i cant give full code, as i have no ide at here, its internet machine at work. I could not give details therefore.
In my opinion, the simplest way is 3-rd option - have both query and insert methods in same class. And you can do something like that:
public String query () {
//...
}
public String Insert() {
//insert
return Query(); }
If your classes are managed Beans you can load class A from class B and call A.query() in your insert method at the end. Also class A can have
<managed-bean-scope>session</managed-bean-scope>
parameter in faces-config.xml and it wouldn't be instantiated again when loaded.