How to preserve a object through multiple pages - java

I have an ArrayList of objects and I use them as follow:
<ui:repeat value="#{BookReview.books}" var="book" >
<li>
<h:commandLink value="#{book.bookName}" action="detail" />
</li>
</ui:repeat>
in the detail page I show the book's comments, and I have a form to register a new comment:
<h:form>
Name:<h:inputText value="#{Comment.author}" />
Rate:<h:inputText value="#{Comment.rate}" />
Text:<h:inputText value="#{Comment.comment}" />
<h:commandButton action="#{book.addComment(Comment)}" value="Add Comment" />
</h:form>
My problem is that book isn't preserved until the next Request and #{book.addComment(Comment)} results in Target Unreachable, identifier 'book' resolved to null.
I have tried annotate the book as RequestScoped, didn't work, then I've modified to ViewScoped, didn't work either, then I tried to use a <inputHidden > to keep the object around, but it just uses a .toString() and I can't reuse the object.
I don't want to use session to store the object because I need it only once, and I don't think I want to use a converter because Book has a ArrayList of comments (also I think it is to cumbersome and complicated)

Put your BookReview bean to the session scope (or, if you're on JSF-2.0 - then let it have the view scope).

You've got to keep a handle of the current book somewhere and relate the new comment with it.
Create a new bean, BookDetail.
#ManagedBean
#ViewScoped
public class BookDetail {
private Book book;
private Comment comment = new Comment();
public String addComment() {
book.getComments().add(comment);
// You need to persist book here if necessary.
return "list";
}
// Add/generate getters/setters the usual way.
}
Set the selected book as follows (could be done nicer if you were using an UIData component like h:dataTable or t:dataList instead of ui:repeat):
<h:commandLink value="#{book.name}" action="detail">
<f:setPropertyActionListener target="#{bookDetail.book}" value="#{book}" />
</h:commandLink>
Rewrite the detail form as follows:
<h:form>
Name:<h:inputText value="#{bookDetail.comment.author}" />
Rate:<h:inputText value="#{bookDetail.comment.rate}" />
Text:<h:inputText value="#{bookDetail.comment.comment}" />
<h:commandButton action="#{bookDetail.addComment}" value="Add Comment" />
</h:form>

Related

How to correctly call method from jsf to ManagedBean

well I'm learning jsf/hibernate and I'm having trouble understanding how to correctly call methods to my ManagedBean
its a simple CRUD so I need to pass vía the jsf form a Ciclista object, but since the inputs are Ciclista attributes how do I pass the Object ? (how to create instance of it) here's the code:
#Override
public String create(Ciclista c) {
Session s = sFac.openSession();
s.beginTransaction();
s.save(c);
s.getTransaction().commit();
s.close();
return "Administrador?faces-redirect=true";
}
and jsf form:
<h:form class="form">
<div id="input-wrapper">
<h:inputText class="inputs" value="#{ciclistaBeanDB.nombre}" />
<h:inputText class="inputs" value="#{ciclistaBeanDB.nacionalidad}" />
<h:inputText class="inputs" value="#{ciclistaBeanDB.equipo}" />
</div>
<h:commandButton class="btn" action="#{ciclistaBeanDB.create()}" value="Create" />
</h:form>
I get error: method not found because i don't pass the Ciclista c object in crear(//here)
since i don't have the object per se, just its attributes how do I proceed ??
You are getting the MethodNotFoundException, because you wrote crear instead of Create.
But the right way to do it, would be to call a save() method without arguments, which is responsible to assemble your object you want to persist.
Since you are already binding your input fields to bean properties, all you would need to do is, use a method like this:
public String save(){
Ciclista c = new Ciclista();
c.setNombre(this.nombre);
c.setNacionalidad(this.nacionalidad);
c.setEquipo(this.equipo);
create(c);
return "Administrador?faces-redirect=true";
}
your commandbutton now should target the save() method of your bean. The input values in the form will be inserted into your baking bean properties and the save() method would assemble the instance and finally calls your persist-method.
<h:commandButton class="btn" action="#{ciclistaBeanDB.save}" value="Create" />
Usually you should not call the DatabaseBean directly, but kind of a Controller - or how ever one would like to call it. This Bean then should utilize your database-service which should only be responsible for CRUD-Functionality.
At least for big applications it would be a mess having the databasemethods and other (view-related) methods mixed in one class.

Primefaces cannot update p:tree

I'm working with primefaces 4.0 and JSF 2.2 and I'm currently trying to update a form with a p:tree on it. The commandButton works correctly but it does not update the form or call the init() method until I manually refresh the page. I don't know what I'm doing wrong since the same code does work for a DataTable element.
Here's the code:
<h:form id="preferenciasForm">
<div id="panelTree">
<p:panel id="defTree" style="margin-bottom: 20px">
<p:tree value="#{dtPreferencesBuilder.root}" var="node"
selectionMode="checkbox"
selection="#{dtPreferencesBuilder.selectedNodes}"
style="width:100%; height:100%;" animate="true">
<p:treeNode>
<h:outputText value="#{node.label}" />
</p:treeNode>
</p:tree>
<p:commandButton value="Add preferences"
icon="ui-icon-pencil"
actionListener="#{dtPreferencesBuilder.insertPrefNodes()}"
update=":preferenciasForm" ajax="true" />
</p:panel>
</div>
</h:form>
And here's is the java class.
#ManagedBean(name="dtPreferencesBuilder")
#ViewScoped //I've tried with or without the ViewScoped, neither work
public class PreferencesBuilderBean {
private TreeNode root;
private TreeNode prefRoot;
private TreeNode[] selectedNodes;
#PostConstruct
public void init() {
System.out.println("Building Tree");
selectedNodes=null;
root=null;
prefRoot=null;
root=getStandardTree();
prefRoot=getPreferedTree();
}
The init() is not called as the print is only show on manual reload so the tree is not updated nor the selectedNodes refreshed. Any ideas why it doesn't work?
As I cannot describe bean scopes better than excellent answers already given for similar questions I'll just refer you to the answers by BalusC and Kishor P here.
The init-method (or any method with the #PostConstruct-annotation) will be called by the framework only when the bean is created, after injections and therefore after the constructor has run as rion18 said. It would not be normal to use the method for anything else than initializing work. So create other methods, and call those from actions and actionListeners.
If you want the bean to be the same when you call it with ajax (as you do) it needs to be at least ViewScoped. If you really do want to call the init() every time it should be RequestScoped, but then the bean will be new when you call it with ajax and not remember a thing.

f:ajax listener isn't called

I've got a problem considering JSF and AJAX.
I am trying to update some customer details after a visitor inserts the customer id.
Firstly, a excerpt from the xhmtl code:
<h:form>
<h:panelGrid columns="2">
<h:panelGrid columns ="2" border="1" id="customer_grid">
<h:outputLabel value="#{mbean_msg.reservation_lblCustomerNo}" for = "customer_id"/>
<h:inputText id = "customer_id" value="#{reservationHandler.customer.customer_id}">
<f:ajax listener="{reservationHandler.autocompleteCustomerDetails}"
render="customer_grid" />
</h:inputText>
<h:outputLabel value="#{mbean_msg.reservation_lblLastname}" for="lastname"/>
<h:inputText id="lastname" value="#{reservationHandler.customer.lastname}" required ="true"
requiredMessage="#{error_msg.errmsgLastname}" validator="#{reservationHandler.validateCustomer}"/>
<h:outputLabel value="#{mbean_msg.reservation_lblFirstname}" for="firstname"/>
<h:inputText id="firstname" value="#{reservationHandler.customer.firstname}" required ="true"
requiredMessage="#{error_msg.errmsgFirstname}" validator="#{reservationHandler.validateCustomer}"/>
The listener method is implemented within my java file (ReservationHandler.java) like that:
public void autocompleteCustomerDetails(){
System.out.println("Auto Complete"); // for testing
}
Basically I am trying to call the method autocompleteCustomerDetails with the Listener. Unfortunately this method is never called. Anyways, the render seems to work just fine, since the other inputTexts update themselves (visibly).
Does anybody have an idea, why the listener isn't called?
There are two problems in the code shown so far:
First,
<f:ajax listener="{reservationHandler.autocompleteCustomerDetails}" />
this isn't a valid EL expression. EL expessions have the form of #{}, not {}. Fix it accordingly:
<f:ajax listener="#{reservationHandler.autocompleteCustomerDetails}" />
Second,
public void autocompleteCustomerDetails() {
this isn't a valid default signature of a method expression for <f:ajax listener>. The tag documentation clearly tells the following:
signature must match public void processAjaxBehavior(javax.faces.event.AjaxBehaviorEvent event) throws javax.faces.event.AbortProcessingException.
So, you forgot the argument. Add it accordingly. The throws declaration isn't mandatory for unchecked exceptions, so we can just leave it out.
public void autocompleteCustomerDetails(AjaxBehaviorEvent event) {
Or, if you actually intend to get rid of the argument, then you should put parentheses in the EL method expression:
<f:ajax listener="#{reservationHandler.autocompleteCustomerDetails()}" />
Note that this works only if your container supports EL 2.2.
Try this:
<f:ajax listener="{reservationHandler.autocompleteCustomerDetails()}"
render="customer_grid" />
or change the method to:
getAutoCompleteCustomerDetails
just for testing...
Anyhow, you´re not allowed to use any jsf's components library? Like primefaces ?
It gets the job done in such a easy way...
Anyhow, with primefaces I would do that like this:
<p:ajax event="blur" listener="#{prospectoRadarController.atualizarRadar(data)}" update=":mainForm:painelRadar" />
Another guess would be, add the execute="#this" to the ajax flag...
Sorry, those are all wild guesses, but I really want to help.
PLease feedback!

How can I transport an entity from one jsf-view to another?

I have the following structure:
listView.xhtml
<h:dataTable value="#{listBean.myList} ...>
//for every row I create a commandLink
<h:commandLink action="editView" value="edit" />
</h:dataTable>
ListBean.java
#ManagedBean
#ViewScoped
public class ListBean{
public List<Entity> myList; // also getters and setters
}
editView.xhtml
<h:inputText value="#{editBean.selectedEntity.name}" />
EditBean.java
#ManagedBean
#ViewScoped
public class EditBean{
public Entity selectedEntity; // also getters and setters
}
You know the question: How can I transport the selected Entity from listView to editView? This should be very simple I thought, but after a whole day, I didnt get it work.
I tried different stuff, like #ManagedProperty and <f:param name="" value=""> but I didnt help me.
So please, show me how simple and nice this can be :)
Thanks in advance!
UPDATE - Solution#1
Thanks to Daniel,
a possible way that works is, when the entity is hold by an EntityManager, so you can access the entity by its id. So you will pass the id as an request Parameter. Here we go:
listView.xhtml
<h:dataTable value="#{listBean.myList} ...>
//for every row I create a commandLink, so you can click on that entity to edit it
<h:commandLink action="editView" value="edit">
<f:param name="selectedEntityId" value="#{entity.id}" />
</h:commandLink>
</h:dataTable>
EditBean.java
#ManagedBean
#ViewScoped
public class EditBean{
private Entity selectedEntity;
#PostConstruct
public void init() {
Map<String, String> params = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap();
long selectedEntityId = Long.parseLong(params.get("selectedEntityId"));
selectedEntity = SomeEntityManagerUtil.getEntity(selectedEntityId);
}
}
The general idea could be :
To pass an id of that entity and get an entity by that id later on...
You also could use a converter and inside it translate that id into entity...
like this :
<h:inputText value="#{editBean.selectedEntity.name}" converter="myEntityConverter"/>
Perhaps you should merge your beans if they have the same scope ?
You may also use the context:
jsf-get-managed-bean-by-name
Also look at this question:
passing-data-between-managed-components-in-jsf

selectOneMenu with selectItems

I am trying to make a filter using selectOneMenu.
I have two categories, when one is selectect, must filter the results shown, and also the second category.
JSF code:
<div id="box-categories" class="box-left">
<b>Filter by topic</b>
<h:selectOneMenu id="cat1" binding="#{interfaceContainer.documentFormContainer.selectOnes['cat1'].selectOne}" rendered="true" onchange="javascript:refreshResults(); return false;">
<f:selectItems value="#{interfaceContainer.documentFormContainer.selectOnes['cat1'].items}" />
</h:selectOneMenu>
<b>and subtopic</b>
<h:selectOneMenu id="cat2" binding="#{interfaceContainer.documentFormContainer.selectOnes['cat2'].selectOne}" rendered="true" onchange="javascript:refreshResults(); return false;" value="#{interfaceContainer.documentFormContainer.selectOnes['cat2'].value}">
<f:selectItems value="#{interfaceContainer.documentFormContainer.selectOnes['cat2'].items}" />
</h:selectOneMenu>
</div>
But I have problems when I try to get the values using this java code:
public String getStringValue(){
if ( this.selectOne ==null || this.getSelectOne().getValue()==null)
return "";
return this.getSelectOne().getValue().toString();
}
I realised that the problem is just with getValue(), because debugging, this.getSelectOne() is in the rigth value, but this.getSelectOne().getValue() is null.
Any idea??
Thanks in advance
UIInput#getValue() will return null when you attempt to access it during any phase before the Update Model Values phase. You're apparently accessing it at the "wrong" moment in the JSF lifecycle. Anyway, creating dependent dropdown menus in JSF without help of a shot of Ajax is terrible.
Long story short, here's how to do it: Populate child menu's (with complete and working code examples).

Categories