I have a page with a dataTable, which is populated based on the query parameters (e.g., username and pagenum). Each entry in the table has a delete commandButton
When the pagenum != 0 and we click delete, the list of records to display is generated during the "apply" phase. During this phase the view parameters have not been set, so the list of records is empty so nothing get's deleted (our delete method doesn't get called)
To work around this I've added a #PostConstruct method that retrieves the query parameters from the Servlet request and sets the values in the bean, so they are available when we get the list of away records, which allows my delete method to be called.
I'm certain that JSF has a better way of handling this scenario and the #PostConstruct work around is a hack.
What is the correct way to implement this scenario, without resorting to a View or Session scoped bean?
Surely there must be a way to just POST the form and delete the appropriate record without having to waste time regenerating the list of records.
What is the correct way to implement this scenario, without resorting to a View or Session scoped bean? Surely there must be a way to just POST the form and delete the appropriate record without having to waste time regenerating the list of records
Sorry, there's no way. At least not when using a standard <h:commandButton> inside a standard <h:dataTable>. This is the consequence of the stateful nature of JSF. JSF just wants to ensure that the view is exactly the same during processing the postback as it was during generating the HTML output.
This is part of JSF's safeguard against tampered requests wherein the enduser/hacker can manipulate the request parameters in such way that it could do hazardful things, e.g. changing the ID of entry to delete, or bypassing the check on rendered attribute, etc. All those things on which you would/should do additional pre-validation anyway if JSF didn't do that for you and are easily overlooked by starters (they would then blame JSF for being insecure instead of themselves). See also Why JSF saves the state of UI components on server? and commandButton/commandLink/ajax action/listener method not invoked or input value not updated.
In case of <h:commandButton> inside <h:dataTable>, JSF simply needs to have the data model available during the apply request values phase, so that it can iterate over the <h:dataTable> in the component tree in order to find the pressed button and queue the action event. If there's no datamodel, then it can't find the pressed button and the action event won't be queued. Normally, this is to be solved by placing the managed bean in the JSF view scope. See also How to choose the right bean scope?
In case of request scoped beans, the <f:viewParam> is indeed not the right tool for the job of preserving the data model before apply request values phase takes place. You need to do the job in a #PostConstruct annotated method instead. The request parameters can in case of JSF managed beans be injected via #ManagedProperty. See also ViewParam vs #ManagedProperty(value = "#{param.id}"). In case of CDI or Spring managed beans, there's no standard annotation available to inject a HTTP request parameter as a bean property. For CDI, the JSF utility library OmniFaces has a #Param for the very purpose. See also OmniFaces #Param showcase. For Spring, you'd need to homegrow it yourself. I, as non-Spring-user have however no idea how to do that. Google also doesn't seem to reveal much.
Alternatively, you can also just put the bean in the view scope by #ViewScoped. It'll then live as long as you postback to the same view. JSF 2.2 has a CDI compatible annotation for that in javax.faces.view package. The one in javax.faces.bean package is the old JSF 2.0/2.1 annotation for #ManagedBean. Spring has no annotation out the box for this as that would otherwise put a dependency on JSF API. You'd need to homegrow it yourself. Google shows several examples.
What is the correct way to implement this scenario
Before executing the any logic on the backing bean, JSF always have to rebuild the view in order to get information about what to execute. For displaying and updating purpose, the best (and correct) solution is certainly the #ViewScoped.
without resorting to a View or Session scoped bean?
If you insist on using #RequestScoped, I'd say there're no correct ways but work-arounds or hacks. One way is to initialise the list in a #PostConstruct method like you've mentioned. Another way may be to use a JavaScript function for the onclick attribute of your delete button. The JS function, for example, will make a call to the server using a URL to request a delete. Or else, you can also use PrimeFace's RemoteCommand for the JS function.
Related
I'm using the #Scope annotation with value of "request".
How do I check if the given object with the "Scope" annotation is instantiated on each http request ?
Do the object (bean) have some identifier (hashcode ) ? And, I don't mean the bean id.
System.identityHashCode(theBeanVariable)
Print the hashes and check the objects are the same or not.
You gotta believe!
No, I'm kidding. Methods I used so far:
In eclipse if you stop application on a breakpoint you can check the id of every object in Variables tab. Every new instance of object has new id. You probably can find a place in your code that is executed after every (or some) request.
If you can set some fields of this bean via a web page, go and do it and then open the same page in new tab in your web browser. If request scope is working, fields you set should have old values (the one that are set on creation of object).
Maybe these are not uber-pro methods, but may be enough in some cases and you don't have to add anything in your code.
I have a single JSP that serves multiple methods in a Controller -- it is the result of Add, Edit, Delete, etc. It may even serve multiple Controllers, so it's used in a lot of places.
This JSP always has a List Box displaying a set of records. So I need to re-fetch the list on every request every time this JSP is displayed.
On the one hand, I can do request.setAttribute("contents",
service.getForms()) and then retrieve this Req. Attr., but this has
to be done on every method that uses the JSP. There may be dozens,
and it's redundant.
Can't use a Session object, the list may change depending some
Add/Delete operations, so it needs to be re-fetched.
I was thinking of scriptlets: in this case, although generally
ill-advised, there's a single JSP, so the List Box can be populated
on the JSP level, without worrying about controllers. But
unfortunately I can't do this:
<% service.getForms(); %>
My service is auto-wired. According to this thread, I can't expose autowired objects to JSPs:
What is the cleanest way to autowire Spring Beans in a JSP?
#Autowired
#Qualifier("Myervice")
private MyService service;
What's the best way to go about it?
Suppose I have a certain operation that should be available to every process running in Spring MVC.
Say string normalization--
i need to run a method that normalizes the string fields before doing anything else on that form/data.
One thing specific to do is, to normalize the String fields on every input form before
they are dispatched to the back-end services. Likewise, that operation (normalization)
should be run on data from the back-end before it is dispatched to the view component.
One way of doing this that I can think of is:
Code a bean doing it-- the normalization. Then, define this bean somewhere at the top in
the context hierarchy of Spring-- ApplicationContext.xml or WebApplicationContext.xml(?),
so that it will be visible and can be used
accross all the processes/servlets in the application.
Then, Whenever and from wherever needed, invoke that method on the bean defined up there.
Or, inject it to the relevant fields in the bean definitions(?)
In this case, is there a way to call it before or during a HandlerMapping is running? if so, how?
Another i can come up with is:
Code a validator (implement Validator) to run that process and "validate" the String fields for you.
But i dont see how this would be of good help.
From what i know, a validator runs on specific object types. I can define that type generically(?)
but then I'm operating on the fields-- not objects as a whole each.
Coding validator(s) seems too costly to me for this use-- even if it is an option here.
I'm new to Spring. pls bear with me on this.
I want to use JSR 303 (Hibernate Validator) in my JSF 2 project. However, I have complicated forms and field-level validation is not sufficient. I need to use many #ScriptAssert annotations at class-level of my model and its child beans.
So I want to validate form models manually (inside bean action method for example). But I could not understand how I can preserve which validation message should be shown at which field (as it works automatically when field-level validation is on and processed by JSF).
Also I'll need to specify for some of class-level annotations that their messages are to be shown at specific fields. I do not see a straight way to manage it...
Could you please provide a link to explanations of these questions (or tell me that I am doing something wrong?). I think I fail in googling it because internet is bloating with keywords JSF and validation, sorry.
The most idiomatic way to do that is to create custom Bean Validation validator for your class.
You need to create validation annotiation that you would put at the class level (not field level) and associated with that class validator class.
See for instance:
http://docs.jboss.org/hibernate/validator/4.1/reference/en-US/html/validator-usingvalidator.html (Example "Class level constraint")
How can I validate two or more fields in combination?
Validation inside managed bean is also possible, you can throw in your action methods proper validation exception, but usually it is cumbersome, hard to reuse and mixes business logic code with validation.
I am trying to share data between two controllers in a Spring mvc application.
In Controller A I have annotated the class with #SessionAttributes({"mymodel1"}) and in the method which receives the first GET request I add it to the ModelMap:
model.addAttribute("mymodel1", MyModel1);
I now want to read myModel1 from Controller B.
In this Controller I have the following method which intercepts the POST requests and already has a different model in its parameters:
public String processSubmit(#ModelAttribute("mymodel2") MyModel2 mymodel2, BindingResult result, SessionStatus status, HttpServletRequest httpRequest)
Up to this point everything works fine and I am able to read mymodel2 from processSubmit however if I now want to add another #ModelAttribute("mymodel1") MyModel1 mymodel1 to this method signature I would have expected to be able to read the value I was setting in Controller A however I'm getting exceptions that the first model is no longer recognised.
So my question is: how can I read mymodel2 from Controller B?
You can't do that with #SessionAttributes :
Session attributes as indicated using this annotation correspond to a specific handlers model attributes, getting transparently stored in a conversational session. Those attributes will be removed once the handler indicates completion of its conversational session. Therefore, use this facility for such conversational attributes which are supposed to be stored in the session temporarily during the course of a specific handlers conversation.
For example I use this annotation when I want to validate elements with Hibernate validation, and after I submit the page and SOME elements are invalid I want the rest to be still on the page, but this is not your case. I think that the only way to do it would be with:
HttpSession.getAttribute()
The javadoc excerpt above is the most typical way #SessionAttributes is used. However, what Joly is describing should also work. Session attributes are stored via DefaultSessionAttributeStore, which by default does not prefix attribute names when it stores them in the session. That means if ControllerA and ControllerB both list an attribute called "mymodel1", they're actually referring to the same session attribute. You'll need to provide a little more information on the error you're getting and the actual controller code.