JSF 2.0 pages, navigate between two pages without loosing data - jsf

I have the following issue:
I have a page on which I create a offer (page1).
Now I have a button on page1 to select a customer (page2).
So I press the button and my new page (page2) appears to select a customer.
With another button I select the customer and redirect to my first page (offer page). But now all my entered values are not there anymore.
I tried the following:
import javax.enterprise.context.ConversationScoped;
import javax.enterprise.context.SessionScoped;
import javax.faces.view.ViewScoped;
My button is the following:
<p:commandButton value="Select customer" ajax="true" process="#all"
actionListener="#{offerEditController.doSelectCustomerForDocument}"
update=":addOfferForm, growl" immediate="true">
</p:commandButton>
And here my method for go to the page2:
public void doSelectCustomerForDocument() {
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
ec.redirect(ec.getRequestContextPath() + Constants.CUSTOMER_LIST
+ "?create_document=/offerEdit.jsf&document_id=" + offer.getId());
}
#SessionScoped works not for all inputFields, e.g.
For
<p:inputText id="offer_subject"
value="#{offerEditController.offer.title}" >
<p:ajax events="blur" update="offer_subject_panel"
global="false" />
</p:inputText>
Any ideas how can I solve this? I know I could use a p:dialog, but I don´t like this.
Thank you for all help.

The better ideia would use a p:dialog, because it fits perfectly on your case but if you don't like it, you should improve the way you call your pages. In your case, when you do the following:
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
ec.redirect(ec.getRequestContextPath() + Constants.CUSTOMER_LIST
+ "?create_document=/offerEdit.jsf&document_id=" + offer.getId())
you force the page to reload and recall the #PostConstruct method, cleaning the whole backing bean (controller). That's why you lose the values entered.
If you want to keep this approach of redirection you have to store the page data somewhere before it get cleaned. I suggest storing at requestContext passing parameters using JSON from page2 to page1 using <p:remoteCommand>, then, when you reload on #PostConstruct method you look for parameters in the requestContext and reset the fields.
Put this on Page2:
<p:commandButton id="buttonOnPage2" onclick = "remoteCommandFunction([{name:'value1',value:'#{bean.value1}'},{name:'value2',value:'#{bean.value2}'}])"/>
<p:remoteCommand name="remoteCommandFunction" actionListener="#{bean.storeData()}"/>
On your bean:
public void storeData(){
Map<String, String> params = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap();
if(params.get("value1") !=null && !params.get("value1").isEmpty())
this.value1= params.get("value1");
if(params.get("value2") !=null && !params.get("value2").isEmpty())
this.value2= params.get("value2");
}
You have to ensure that storeData() method is called after #PostConstruct, if its called after post construct, everything will be null. If this happens you should put the storeData() method in a #SessionScoped bean and retrieve it on your #ViewScoped inside #PostConstruct method like this:
#PostConstruct
public void init(){
...
this.value1 = getSessionScopedBean().getValue1();
this.value2 = getSessionScopedBean().getValue2();
getSessionScopedBean().setValue1(null);
getSessionScopedBean().setValue2(null);
}

Related

ExternalContext#redirect() with includeViewParams=true

Using a list of currencies in the form of strings as follows.
<p:selectOneMenu id="currency"
value="#{currencyRateBean.currency}"
onchange="changeCurrency([{name: 'currency', value: this.value}]);">
<f:selectItems var="row"
value="#{currencyBean.currencies}"
itemLabel="#{row}"
itemValue="#{row}"/>
</p:selectOneMenu>
Along a <p:remoteCommand>.
<p:remoteCommand ignoreAutoUpdate="true"
name="changeCurrency"
partialSubmit="true"
process="#this"
update="#none"
action="#{currency.currencyAction}"/>
The managed bean setting a currency value being passed through the above <p:remoteCommand> as a parameter to a JavaScript function.
#Named
#RequestScoped
public class Currency {
#Inject
#HttpParam
private String currency;
#Inject
private CurrencyRateBean currencyRateBean;
public Currency() {}
public String currencyAction() throws MalformedURLException, IOException {
try (Scanner scanner = new Scanner(new URL("http://www.exchangerate-api.com/INR/" + currency + "/1?k=FQRxs-xT2tk-NExQj").openConnection().getInputStream(), "UTF-8");) {
currencyRateBean.setCurrencyRate(scanner.nextBigDecimal());
currencyRateBean.setCurrency(currency);
} catch (UnknownHostException | ConnectException e) {}
return FacesContext.getCurrentInstance().getViewRoot().getViewId() + "?faces-redirect=true&includeViewParams=true";
}
}
The supplied currency value is then set to another session scoped managed bean CurrencyRateBean from within the action method currencyAction() above which finally makes a redirect based on the current value of viewId along with includeViewParams=true which is important.
Now, the story changes, when #{currencyRateBean.currencies} has been changed to have a list of composite objects which has been a list of Strings so far.
The following scenario will not work with includeViewParams=true which is significant.
<p:selectOneMenu value="#{currencyRateBean.currencyHolder}">
<f:selectItems var="row" value="#{currencyBean.currencies}"
itemLabel="#{row.currency}"
itemValue="#{row}"/>
<p:ajax event="change"
listener="#{currency.currencyAction}"
partialSubmit="true"
process="#this"
update="#none"/>
</p:selectOneMenu>
public void currencyAction() throws IOException {
// ...
FacesContext facesContext = FacesContext.getCurrentInstance();
String viewId = facesContext.getViewRoot().getViewId();
ExternalContext externalContext = facesContext.getExternalContext();
externalContext.redirect(externalContext.getRequestContextPath() + viewId + "?includeViewParams=true");
}
includeViewParams=true has been added for decoration only. It is not going to work.
Since listener in <p:ajax> is incapable of making a redirect based on a navigation case outcome as done by action of <p|h:commandXxx>, ExternalContext#redirect() has to be used anyway.
<p:remoteCommand> can be used on complete of <p:ajax> but this will involve two round trips to the server unnecessarily, first to set the currency value to the associated managed bean and then to make a redirect.
How to make a redirect with includeViewParams=true in the example given?
Like faces-redirect=true, includeViewParams=true works only in navigation outcomes, not in "plain" URLs which you pass to ExternalContext#redirect().
Use NavigationHandler#handleNavigation().
FacesContext context = FacesContext.getCurrentInstance();
String outcome = viewId + "?includeViewParams=true";
context.getApplication().getNavigationHandler().handleNavigation(context, null, outcome);
Or, with OmniFaces.
Faces.navigate(viewId + "?includeViewParams=true");
A dubious alternative is to collect all view parameters yourself and convert them to query string so you can use ExternalContext#redirect() anyway. This is easier with OmniFaces.
Faces.redirect(viewId + "?" + Servlets.toQueryString(Faces.getViewParameterMap()));

JSF h:outputLink call bean method before GET

I need to send some parameters from one xhtml to another, but I don't want these parameters to appear in the URL. How to do that? I could use p:commandLink, but then I don't know how to open the destination page from the bean method. The destination page should be accessible by friendly URL, not by xhtml name.
This code will open the page /users/view. How can I send parameters without them appearing in the URL?
<h:outputLink value="/users/view">
<h:outputText value="#{entry.employee}" />
</h:outputLink>
Ignoring the strange design, you could use put the data in the flash scope and send a redirect in a <h:commandLink> action method:
public void view() throws IOException {
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
ec.getFlash().put("employee", employee);
ec.redirect(ec.getRequestContextPath() + "/users/view");
}
And then in the backing bean associated with the target page:
#PostConstruct
public void init() {
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
employee = (Employee) ec.getFlash().get("employee");
}
See also:
Pass an object between #ViewScoped beans without using GET params
What is the difference between redirect and navigation/forward and when to use what?

Button not submit PrimeFaces 3.5

i'm using jsf + primefaces 3.5. And my button isn't calling one method in my managed bean.
I have this xhtml:
<h:form>
<p:inputText id="name" value="#{userMB.userSelected.name}" />
<p:commandButton id="btnSave" value="Salvar" actionListener="#{userMB.save}"/>
</h:form>
And my managed bean is:
#ManagedBean
#SessionScoped
public class UsuarioMB implements Serializable{
User userSelected;
public void save(){
System.out.println(userSelected.getName());
//call my daos and persist in database
}
}
The most curious is that if i remove the , the method is called!
If i put a atribute in p:commandButton "imediate = true ", the method is called, BUT, the information (userSelected.name) is null !
Thanks very much :)
It failed because it threw a NullPointerException because you never initialized userSelected.
Add this to your bean:
#PostConstruct
public void init() {
userSelected = new User();
}
If you have paid attention to the server logs, you should have seen it. As to the complete absence of feedback about the exception in the webbrowser, whereas in normal synchronous (non-ajax) you would have seen a HTTP 500 error page, it's because you're sending an ajax request without apparently an ExceptionHandler configured.
That it works when you set immediate="true" on the button is simply because it will then bypass the processing of all input components which do not have immediate="true" set.
See also:
What is the correct way to deal with JSF 2.0 exceptions for AJAXified components?
You have not given a name to the managedbean UsuarioMB. As suche it will be named usuarioMB.
#ManagedBean – marks this bean to be a managed bean with the name
specified in name attribute. If the name attribute in #ManagedBean is
not specified, then the managed bean name will default to class name
portion of the fully qualified class name.
read more about it in this blog: http://mkblog.exadel.com/2009/08/learning-jsf2-managed-beans/
Secondly, if your code above is complete, you are lacking public getter and setter for userSelected.
Thirdly you are missing the ActionEvent as you have declared a parameterless actionlistener, see Differences between action and actionListener
In order to get you code working you will need to change your xhtml to
<h:form>
<p:inputText id="name" value="#{usuarioMB.userSelected.name}" />
<p:commandButton id="btnSave" value="Salvar" actionListener="#{usuarioMB.save}"/>
</h:form>
And your managed bean as follows
import javax.faces.event.ActionEvent;
// ...
#ManagedBean
#SessionScoped
public class UsuarioMB implements Serializable{
private User userSelected;
public void save(ActionEvent event){
System.out.println(userSelected.getName());
}
public User getUserSelected() {
return userSelected;
}
public void setUserSelected(User userSelected) {
this.userSelected = userSelected;
}
}

Single page applications with ajax [duplicate]

This question already has answers here:
How to ajax-refresh dynamic include content by navigation menu? (JSF SPA)
(3 answers)
Closed 1 year ago.
I'm relatively new to JSF and trying to learn how current JSF 2 applications are designed. I've seen reference to single page applications that use ajax. Can someone fill me in on some of the techniques used and / or point me to a model or book? The books I've seen (JSF Complete Reference etc.) are good for basic tech issues but I can't find a source for current design techniques.
Thanks
Dave
In order to implement your Single Page Application, you should state which piece of your page should be rendered. This can be accomplished making use of a boolean flag such as create, edit, list, and so on. For instance, see the following (Just relevant code)
<h:body>
<h:form rendered="#{userController.stateManager.create}">
<h:panelGroup rendered="#{not empty facesContext.messageList or userController.stateManager.failure}">
<!--render error message right here-->
</h:panelGroup>
<div>
<label>#{messages['br.com.spa.domain.model.User.name']}</label>
<h:inputText value="#{user.name}"/>
</div>
<h:commandButton action="#{userController.create}">
<f:ajax execute="#form" render="#all"/>
<f:actionListener type="br.com.spa.web.faces.listener.StateManagerActionListener" />
<f:setPropertyActionListener target="#{userController.stateManager.create}" value="true"/>
<f:setPropertyActionListener target="#{userController.user}" value="#{user}" />
</h:commandButton>
</form>
</h:body>
Notice that our form will be rendered when a flag create is true - See second line above. To wrap our flags, we create a classe named StateManager as follows
/**
* I am using lombok, which takes care of generating our getters and setters. For more info, please refer http://projectlombok.org/features/index.html
*/
#Setter #Getter
public class StateManager {
private boolean create;
private boolean edit;
private boolean list;
}
Now, because we are using only a single page, we should use a ViewScoped managed bean, which keep our managed bean scoped active as long as you are on the same view - Is it a single page application, right ? So, no navigation. With this in mind, let's create our managed bean.
#ManagedBean
#ViewScoped
public class UserController implements StateManagerAwareManagedBean {
private #Inject UserService service;
private #Getter #Setter stateManager = new StateManager();
private #Getter #Setter List<User> userList = new ArrayList<User>();
private #Getter #Setter User user;
#PostConstruct
public void initialize() {
list();
}
public void create() {
service.persist(user);
stateManager.setCreate(false);
stateManager.setList(true);
stateManager.setSuccess(true);
}
public void edit() {
service.merge(user);
stateManager.setEdit(false);
stateManager.setList(true);
stateManager.setSuccess(true);
}
public void list() {
userList = service.list();
stateManager.setList(true);
}
}
For each action method, we define which piece of our page should be rendered. For instance, consider that our form was processed, covering all of JSF lyfecycle, which implies that their values was successfully converted and validated, and our action method invoked. By using as example our create action method - see above -, we set its create flag as false because our form was converted and validated, so we do not need to show it again (Unless you want). Furthermore, we set both list and success flag as true, which indicates that the list of our page should be rendered and our form was successfully processed - You could use this flag to show something like "User created" such as bellow
<h:panelGroup rendered="#{userController.stateManager.success}">
#{messages['default.created.message']}
</h:panelGroup>
Now, let's discuss which piece of our page should be rendered when it is called for the first time. Maybe you do not know but a void method annotated with #PostConstruct will be called first. So we define which piece of our page should be rendered. In our example, we call list method, which sets its list flag as true and populate a backing list.
#PostConstruct
public void initialize() {
list();
}
Finally, let's review the following order nested within h:commandButton
<h:commandButton action="#{userController.create}">
<f:ajax execute="#form" render="#all"/>
<f:actionListener type="br.com.spa.web.faces.listener.StateManagerActionListener" />
<f:setPropertyActionListener target="#{userController.stateManager.create}" value="true"/>
<f:setPropertyActionListener target="#{userController.user}" value="#{user}" />
</h:commandButton>
First of all, you should call an ActionListener - here called StateManagerActionListener - which takes care of resetting any StateManager - code bellow. It must be called first before any other setPropertyActionListener designed to control any flag because the order defined within h:commandButton is the order in which they will be called. keep this in mind.
public class StateManagerActionListener implements ActionListener {
public void processAction(ActionEvent e) throws AbortProcessingException {
Map<String,Object> viewMap = FacesContext.getCurrentInstance().getViewRoot().getViewMap();
for(Map.Entry<String,Object> entry: viewMap.entrySet()) {
if(entry.getValue() instanceof StateManagerAwareManagedBean) {
((StateManagerAwareManagedBean) entry.getValue()).setStateManager(new StateManager());
}
}
}
}
StateManagerAwareManagedBean - used in our ViewScoped Managed bean -, which allows that we reset any StateManager of any ManagedBean instead of resetting one by one in our ActionListener, is defined as follows
public interface StateManagerAwareManagedBean {
StateManager getStateManager();
void setStateManager(StateManager stateManager);
}
Second, after defining our ActionListener, we use a setPropertyActionListener which set the flag which controls the enclosing piece of the view as true. It is needed because our form is supposed to be not converted and validated. So, in our action method, we set this flag as false as discussed before.
A couple of notes
User is marked as a RequestScoped ManagedBean so that it can not be injected into a ViewScoped one using a ManagedProperty because its scope is shother. To overcome this issue, i set its value by using a <f:setPropertyActionListener target="#{userController.user}" value="#{user}"> - See our form
Our example use JEE features which need a proper Application Server. For more info, refer http://docs.oracle.com/javaee/6/tutorial/doc/
ManagedBean can play different roles such as a Controller, DTO and so on. When it play a role of a Controller, i prefer suffix its name with Controller. For more info, refer http://java.dzone.com/articles/making-distinctions-between

Adding multiple rows in database table from one JSF page in one go

Can we add multiple rows in the database table from one JSF page in one go?
Actually i want to make an attendance page, i want to take attendance of all employees in one JSF page in one go that is when user will press save button then all employees attendance will get save in database.
Is it possible to achieve this using managed bean and JSF page? Kindly give me some idea that how it can be achieved?
I've done insert,view,update and delete for single row.
Yes, just use a <h:dataTable>. Imagine that you've a bean like this
#ManagedBean
#ViewScoped
public class EmployeeManager {
private List<Employee> employees;
#EJB
private EmployeeService employeeService;
#PostConstruct
public void init() {
employees = new ArrayList<Employee>();
employees.add(new Employee());
employees.add(new Employee());
employees.add(new Employee());
// ...
// Do whatever you find necessary. Maybe offer an `Add row` button?
}
public void save() {
employeeService.save(employees);
// ...
}
// ...
}
then you can present it as follows
<h:form>
<h:dataTable value="#{employeeManager.employees}" var="employee">
<h:column><h:inputText value="#{employee.firstname}" /></h:column>
<h:column><h:inputText value="#{employee.lastname}" /></h:column>
<h:column><h:inputText value="#{employee.email}" /></h:column>
</h:dataTable>
<h:commandButton value="Save" action="#{employeeManager.save}" />
</h:form>
See also:
The benefits and pitfalls of #ViewScoped - contains a CRUD example

Resources