I have a xhtml page with a session scoped backing bean. Now i have to open this page in a new tab with right click open link in new tab.
When this page opens the model class of the backing bean must be cleared.
I have used the following code:
<p:menuitem value="Details" action="#{beanMB.clearDetailModel()}"/>
backing bean code:
public void clearDetailModel()
{
memberModel=null;
......
return "/pages/member/MemberDetails.xhtml?faces-redirect=true";
}
The above code clears the session scoped model but it does not open the page in new tab.
Is there any method to open the page in new tab with the above code or is there any alternate way for the above problem?.Any help would be appreciated.
Thank you.
You can try this:
You need to put the following code in MemberDetails.xhtml page.
<f:metadata>
<f:event type="preRenderView" listener="#{beanMB.clearDetailModel}"/>
</f:metadata>
and use url in menuitem to open your page.
<p:menuitem value="Details" url="/pages/member/MemberDetails.xhtml" />
you need to modify your backing bean code as follows:
public void clearDetailModel()
{
if (isNewRequest()){
memberModel=null;
......
}
}
public boolean isNewRequest() {
final FacesContext fc = FacesContext.getCurrentInstance();
final boolean getMethod = ((HttpServletRequest) fc.getExternalContext().getRequest()).getMethod().equals("GET");
final boolean ajaxRequest = fc.getPartialViewContext().isAjaxRequest();
final boolean validationFailed = fc.isValidationFailed();
return getMethod && !ajaxRequest && !validationFailed;
}
Here method isNewRequest() checks if the request is new or not if this is not checked your method clearDetailModel() will be called each time new request is made.
Related
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);
}
I am using a Bean (MenuBean) to dynamically create a
<p:breadCrumb model="#{menuBean.model}" />
When transitioning through each page of my application, I push or pop pages from a "page stack", so as to adapt the breadCrumb of each page.
#ManagedBean
#SessionScoped
public class MenuBean implements Serializable {
private MenuModel model;
private int navIdx;
private ArrayList<String[]> pageStack;
#PostConstruct
public void init() {
pageStack = new ArrayList<>();
navIdx = 0;
String [] home = new String[2];
home[0] = "Home";
home[1] = "home.xhtml";
pageStack.add(home);
refreshModel();
}
private void refreshModel() {
model = new DefaultMenuModel();
int i = 0;
for(String[] s: pageStack) {
DefaultMenuItem index = new DefaultMenuItem();
index.setValue(s[0]);
index.setCommand("#{menuBean.navigate}");
index.setParam("index", String.valueOf(i++));
model.addElement(index);
}
}
private void push(String [] page) {
pageStack.add(page);
navIdx++;
refreshModel();
}
private String pop() {
pageStack.remove(navIdx--);
String[] prevPage = pageStack.get(navIdx);
refreshModel();
return prevPage[1];
}
}
In order to maintain this stack, my MenuBean is SessionScoped.
However, when I refresh the last page for example, the initial page is shown (since the url does not change) but the breadCrumb still displays the whole stack.
My question is: Is there a way to clear the MenuBean when the user leaves the page, or refreshes it?
One way you could refresh the bean is using remoteCommand with set auto run on true. So once you refresh the page remote commad will fire and call method in backing bean.
<!-- init bean on page load -->
<h:form id="initializePageDataForm">
<p:remoteCommand name="initializePageDataCommand" autoRun="true" action="#{MenuBean.init()}" />
</h:form>
This worked for me using JSF instead of Primefaces.
<!-- init bean on page load -->
<f:metadata>
<f:event type="preRenderView" listener="#{MenuBean.init() }" />
</f:metadata>
I am trying to display HtmlInputText dynamically in a JSF page. However, I am getting
javax.faces.FacesException: Cannot add the same component twice: j_idt10:hitDyn
During the first request to the page the input text renders well. That exception happens during postback of the page, when I enter some text in the input component and press Enter.
In the .xhtml page, I have the following code:
<h:form>
<h:outputLabel value="Welcome!"></h:outputLabel>
<f:metadata>
<f:event type="preRenderView" listener="#{dynamicBacking.addDynComp}" />
</f:metadata>
<h:panelGroup id="dynOuter"></h:panelGroup>
</h:form>
In the backing bean, I have the following code:
#ManagedBean(name="dynamicBacking")
public class DynamicBacking {
public void addDynComp() {
Application app = FacesContext.getCurrentInstance().getApplication();
HtmlInputText hit = (HtmlInputText)app.createComponent(HtmlInputText.COMPONENT_TYPE);
hit.setId("hitDyn");
UIComponent parent = findComponent("dynOuter");
if( parent != null ) {
parent.getChildren().add(hit);
}
}
public UIComponent findComponent(final String id) {
FacesContext context = FacesContext.getCurrentInstance();
UIViewRoot root = context.getViewRoot();
final UIComponent[] found = new UIComponent[1];
root.visitTree(new FullVisitContext(context), new VisitCallback() {
#Override
public VisitResult visit(VisitContext context, UIComponent component) {
if(component.getId().equals(id)){
found[0] = component;
return VisitResult.COMPLETE;
}
return VisitResult.ACCEPT;
}
});
return found[0];
}
}
I guess that there is some problem with restoring the state of the dynamic component in a postback. Am I adding the dynamic component too late in the lifecycle of the JSF page? I know that in ASP.NET I could add a dynamic control during Page.Load phase. But I can't so far figure out how to achieve the same in JSF. Please, help!
The exception appears because the component is added in the tree on the initial page load. When performing a postback your listener gets called again and it tries to add another component with the same id and this causes the exception. A solution of the issue is to check if the request is NOT a postback when adding the component. The following code shows how to check for postback:
if (FacesContext.getCurrentInstance().isPostback()) {....
I'm wondering what the best practices are to pass data (an object) between two ViewScoped beans.
They need to be view scoped because of the problem that's brilliantly explained here (to put it short: In both views I'm using a h:commandLink from within a h:dataTable which requires the data model to still be present when submitting).
My problem now is that clicking the link also navigates to a new view, so using the following code, my object gets passed but the DetailViewController instance gets killed right after that and a new one is created when the view changes (as you would expect).
View:
<h:dataTable value="#{searchController.dataModel}" var="item">
...
<h:column>
<f:facet name="header">Action</f:facet>
<h:commandLink id="open" value="open" action="#{searchController.showDetail(item)}" />
</h:column>
</h:dataTable>
Bean:
#ManagedBean
#ViewScoped
public class SearchController {
#ManagedProperty(value="#{detailViewController}")
private DetailViewController detailViewController;
// getters, setters, etc. ...
public String showDetail(Item i) {
detailViewController.setItem(i);
return "view_detail.xhtml";
}
}
How would you solve this? I thought about putting the object inside Flash: FacesContext.getExternalContext.getFlash()... Is there an easier or more elegant solution?
You can use view parameters. (See How do you pass view parameters when navigating from an action in JSF2?)
Typically, your method return the url with query parameters:
public String showDetail(Item i) {
return "view_detail.xhtml?id="+i.getId();
}
And in your view_detail.xhtml file, you add a f:viewParam tag evaluating to on of your bean field:
<f:metadata>
<f:viewParam name="id" value="#{myBean.id}" />
</f:metadata>
Then from your backing bean, you use that field to get your Item instance in your #postConstruct method.
If you don't use the f:viewparam tag, you can also fetch the request parameters to obtain the id.
private String id;
private Item item;
#PostConstruct
public void init() {
if (id != null) {
item = fetchItem(id);
} else {
FacesContext facesContext = FacesContext.getCurrentInstance();
ExternalContext externalContext = facesContext.getExternalContext();
Map<String, String> requestParameterMap = externalContext.getRequestParameterMap();
if (requestParameters.containsKey("id")) {
id = requestParameters.get("id");
item = fetchItem(id);
} else {
throw new WebServiceException("No item id in request parameters");
}
}
}
I have a page which takes in request params for place, then generate information,
for example, http://example.com/xxx/weather.jsf?place=california.
The purpose of doing this is to let user bookmark the link.
In the weather.jsf, there are two outputtext and a commandlink:
Humidity : <ice:outputText value="#{weatherBean.humidity}"/>
Visibility : <ice:outputText value="#{weatherBean.visibility}"/>
<ice:commandLink id="likeButton"
value="Like"
actionListener="#{weatherBean.doLike}" />
In the managedBean:
#ManagedBean(name="weatherBean")
#RequestScoped
public class WeatherBean
{
String humidity;
String visibility;
int numLike;
#PostConstruct
public void init()
{
System.out.println("init called");
HttpServletRequest request= (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
String place = request.getParameter("place");
setHumidity(WeatherDao.getHumidity(place));
setVisibility(WeatherDao.getVisibility(place));
setNumLike(GeneralDao.getNumLike());
}
public void doLike(ActionEvent event)
{
System.out.println("doLike called");
GeneralDao.addNumberLike();
}
}
Alright, the page generated perfectly.
However, when I click the doLike commandLink,
it always triggers the init method first, then call doLike method.
Since the request param is empty, all the other values reset.
Is there any way to prevent a refresh of the page or calling of init method?
I tried partialsubmit or immediate, but no luck.
Your bean is #RequestScoped, so after executing the JSF lifecycle, your bean instance is lost, until the next request comes in, at which point you get a new instance of your bean, and the PostContruct re-executes.
Try changing the scope of your bean to something longer lived, like #ViewScoped.