How to update the JSF sessionscoped managed bean if i access it? - jsf-2

As title.
The problem is the attribute in the bean is fixed after init().
I want to update the count attribute when ever i access #{managedBean.xyz} method in JSF
I want to stick with the sessionscoped instead of view/request because it saves some time for the Object re-creation.
I don't want to do the attribute update manually in every xyz function. thanks

If I understand you correctly, you want to invoke a bean method on every view which involves the bean?
Add <f:event type="preRenderView"> to those views.
<f:event type="preRenderView" listener="#{managedBean.countUp}" />
with
public void countUp() {
count++;
}
It will be invoked only once on every request.

Related

How to trigger destruction of viewscoped bean?

I have a #ViewScoped-annotated managedbean whose #PostContruct-method fetches a list from database to be displayed in a table in the view.
Now when I delete an item I want the changes to be seen in the view.
To keep this dynamic and reusable I only want to delete from database (not manually from list). So I need to destroy/recreate the bean I suppose. Now I do this by navigating to the same view. But the way I do is not reusable.
Can I just destroy the bean manually or navigate to the same view without explicitly navigating to THAT specific view (reusability)?
I am using JSF 2.1
You're already on the right track. viewMap is just like any other map; You can remove a ViewScoped bean by name. Please excuse the atrocious chaining:
FacesContext.getCurrentInstance().getViewRoot().getViewMap().remove("yourBean");
One solution I found is to destroy the bean by
FacesContext.getCurrentInstance().getViewRoot().getViewMap().clear();
I am not sure if this is the way to go because it simply destroys every viewscoped bean. It's not that bad in this case, but doesn't feel clean.
I appreciate any thought on that or alternative solutions.
When you return a non null value inside a method from an action attribute the Bean gets recreated.
index.xhtml
...
<h:commandButton value="delete" action="bean.delete" />
...
Bean.class
...
public String delete() {
// do operations
return "index.xhtml?faces-redirect=true";
}
...

evaluate jsf bean property based on URL

Is there a way to display a specific JSF page based on the request URL?
Let's say I have a JSF page "details.xhtml". The managed bean "detailsBean" has a list of objects where each object has its own ID. Now if a user requests the page "../details.xhtml?id=1", the list should be queried for an object with ID 1 and the resulting details page of this object should be displayed.
I already wrote a converter implementation class which can convert from object to ID and vice versa, but I don't know how to use it properly. Do I have to work through the JAX-RS specification for this to work or is there a more simple solution?
In JSF you can do this by using a so-called view parameter. You declare these in the metadata section of your Facelet:
<f:metadata>
<f:viewParam name="id" value="#{yourBean.yourObject}" label="id"
converter="yourObjectConverter"
/>
</f:metadata>
This will grab the URL parameter id from the request URL. E.g. if you request the page this appears on with localhost:8080/mypage.jsf?id=1, then 1 will be handed to the yourObjectConverter and whatever this converter returns will be set in yourBean.yourObject.
Your backing bean will thus get the converted object. No need to pollute your backing bean over and over again with the same query code.
#ManagedBean
public class YourBean {
private SomeObject someObject;
public void setYourObject(SomeObject someObject) {
this.someObject = someObject;
}
}
If your backing bean is view scoped, you may want to use the OmniFaces variant of viewParam instead, since otherwise it will needlessly convert after each postback (if your converter does a DB query, you definitely don't want this).
Working full examples:
http://code.google.com/p/javaee6-crud-example/source/browse/WebContent/user_edit.xhtml
http://code.google.com/p/javaee6-crud-example/source/browse/src/backing/UserEdit.java
Further reading:
Communication in JSF 2.0 - Processing GET request parameters
Stateless vs Stateful JSF view parameters
You can achieve this with plain JSF with the following steps
Capture the ID in the request to determine what object is being queried for in your DetailsBean from the request parameter. There are many ways to achieve this, one of which is adding the following annotation to your managed bean (this is currently only permitted for a #RequestScoped bean, see why here).
#ManagedProperty(value="#{param.id}")
int requiredObjectId;
The annotation above will capture the id parameter from the request and assign it to the requiredObjectId variable.
Using the captured Id, setup your object in your bean in a #PostConstruct method
#PostConstruct
public void queryForObject(){
//use the requiredObjectId variable to query and setup the object in the backing bean
}
The object retrieved should be assigned as an instance variable of your managed bean
In your view, you could then reference the queried object that has been setup in the backing bean
<h:panelGrid columns="2">
<h:outputText value="Title"/>
<h:outputText value="#{detailsBean.selectedObject.title}"/>
</h:panelGrid>
If your bean is in a scope broader than the request scope, you'll need a combination of constructs to cleanly pull that request parameter before view rendering.
Capture the request parameter within the JSF view itself using
<f:metadata>
<f:viewParam name="id" value="#{detailsBean.requiredObjectId}" required="true" requiredMessage="You must provide an Object Id"/>
</f:metadata>
**OR**
Due to the nature of JSF Lifecycle processing, doing the above alone may not make the value available for your use in time for object setup. You could use the following instead.
<f:metadata>
<f:event type="preRenderView" listener="#{detailsBean.setObjectId}" />
</f:metadata>
What we've done here is specify a method (that captures the id) in the backing bean that must be executed before the view is rendered, ensuring that the id parameter is available as at the time you need it. Proceed with step 3, only if you're using <f:event/> above.
In the backing bean, you now define the setObjectId method
public void setObjectId(){
Map<String,String> requestParams = FacesContext.getExternalContext().getRequestParameterMap();
requiredObjectId = Integer.parseInt(requestParams.get("id"));
}
Note that the above option is generally a work around/hack and not a clean solution as such

JSF bean: call #PostConstruct function after ViewParam is set

I have a product.xhtml and a ProductBean. I use /product/{id} to access the products so I have a viewParam in product.xhtml with value=ProductBean.id. The problem is that inside the bean I use an init function with a PostConstruct annotation in order to fill the details of the product. To do this I need the id to call an external function. I guess though that init is called before viewParam sets the id of the bean and therefore inside init I cannot call the external function because id is not set yet. What am I doing wrong and how do I fix this?
UPDATE
I found what was wrong. I think the viewParam method works with CDI beans but the ManagedProperty method works with JSF beans..
I do have one other problem now. My CDI bean is RequestScoped and when the product.xhtml is rendered the bean is created and I guess is later discarded. The funny thing is that I have a function inside that bean which when I call, I can read the id (which I assume this happens because is connected to the view param) but not any other properties. Any ideas how to fix this?
You need a <f:event type="preRenderView"> instead.
<f:metadata>
<f:viewParam name="foo" value="#{bean.foo}" />
<f:event type="preRenderView" listener="#{bean.onload}" />
</f:metadata>
With
public void onload() {
// ...
}
Note that this is in essence a little hack. The upcoming JSF 2.2 will offer a new and more sensible tag for the sole purpose: the <f:viewAction>.
<f:metadata>
<f:viewParam name="foo" value="#{bean.foo}" />
<f:viewAction action="#{bean.onload}" />
</f:metadata>
See also:
ViewParam vs #ManagedProperty(value = "#{param.id}")
Communication in JSF 2.0 - Processing GET request parameters

Is there any way to have a bean in ViewScope and RequestScope at same time

I have a table of Items, and in each row there is a link that forwards to the edit item page. To load data in the edit page I need the managed bean in request scope (if I put it in view scope I loose data in the forward).
To use ajax in the edit page I need the managed bean in view Scope due to some values I must keep. If I were working with JSF 1.0 and RichFaces I would do it with request scope and a4j:keepalive.
How do I get this funcionality with PrimeFaces and JSF 2.0 or how can I redefine the interface to get this?
Ok, finally based on the post below this is what worked for me:
CommandButton in the Items table:
<o:commandButton id="editButton"
action="#{itemTableMB.editItem(item.id)}" styleClass="botonTabla">
<h:graphicImage styleClass="imagenBotonTabla" url="/resources/images/icons/pencil.png"/>
</o:commandButton>
Action in the managed bean:
public String editItem(Integer id){
return "/pages/items/edit.xhtml?faces-redirect=true&id="+id;
}
edit.xhtml:
<f:metadata>
<f:viewParam id="id" name="id" value="#{itemMB.item.id}" required="true">
</f:viewParam>
<f:event type="preRenderView" listener="#{itemMB.loadItem}" />
</f:metadata>
Listener in itemMB:
public void loadItem(){
this.item = this.itemManager.get(this.item.getId());
}
To load data in the edit page I need the managed bean in Request scope (If I put it in view scope I loose data in the forward).
Are you using a navigation rule (or implicit navigation) without a redirect?
One solution would be to put backing beans of both the "table of items"-page and the "edit item"-page in view scope, and then go from the first to the second one directly via a GET request (e.g. using <h:link>) or a POST/redirect with a request parameter representing the row on which the user clicked.
Use <f:viewParam> on the second page to conveniently convert the request parameter back to an entity representing the item being edited.
If you were indeed using navigation without redirect, then this has the additional benefit that you won't suffer from the notorious 'one-URL-behind-problem', which can be rather confusing to users of your application and be a nightmare for support.

Using f:event to inject a ConversationScoped bean into a ViewScoped bean

I can't inject a ConversationScoped bean into a ViewScoped bean, because the ConversationScoped bean could be shorter lived than the ViewScoped one, or vice versa, depending on whether or not the ConversationScoped bean is long-lived.
To get over this limitation, I tried using an f:event to perform the injection as a preRenderView listener:
<f:metadata>
<f:event type="preRenderView" listener="#{taskController.initializeTask(workPackageConversation.workPackage)}" />
</f:metadata>
This howver is not working, neither the listener initializeTask, nor the getter getWorkPackage are being called.
I realize I can lookup one managed bean from another, using the FacesContext, but I am curious why this isn't working. Is it because the f:event listener isn't called when I navigate to a view from another view? ie. without a redirect or direct page view?
I also tried the s:viewAction tag from Seam 3 Faces, to no avail. It does not get called either.
Thanks in advance.
I would think the lifetime issues would not come into play since you've always got a proxy to the normal-scoped bean anyway. You either dereference the conversation-scoped bean while the conversation is active, or it's not active -- but you'll always get the right conversation.

Resources