I my Java EE application I have articles administration. There is url like http://localhost:8080/articles/detail.xhtml?article=70 where article means id of article. This page displays article comments etc. it is not important. But there is button Edit and I would like to redirect on page edit.xhtml but user should still see http://localhost:8080/articles/detail.xhtml?article=70 because I don't want the edit page to be bookmarkable.
Can you help me how to set up faces-config to change page but not url? I thought that if I don't write <redirect /> then url would stay same but I was wrong. Url changes from detail.xhtml?article=70 to detail.xhtml
Thanks for any advise.
I'd suggest to bring in some ajaxical powers so that no synchronous request is fired.
<h:panelGroup id="article" layout="block">
<h:panelGroup rendered="#{!bean.editmode}">
View article (can if necessary be an ui:include)
<h:form>
<h:commandButton value="Edit" action="#{bean.edit}">
<f:ajax render=":article" />
</h:commandButton>
</h:form>
</h:panelGroup>
<h:panelGroup rendered="#{bean.editmode}">
Edit article (can if necessary be an ui:include)
<h:form>
<h:commandButton value="Save" action="#{bean.save}">
<f:ajax render=":article" />
</h:commandButton>
</h:form>
</h:panelGroup>
</h:panelGroup>
Bean:
private boolean editmode;
public void edit() {
this.editmode = true;
}
public void save() {
this.editmode = false;
}
public boolean isEditmode() {
return editmode;
}
Related
I have a very simple JSF 2/Facelets page that looks like this:
<ui:repeat value="#{myBean.names}" var="_name">
<h:commandLink value="#{_name}" action="#{myBean.sayHello(_name)}">
<f:ajax execute="#this"/>
</h:commandLink>
<br/>
</ui:repeat>
The backing bean provides a java.util.List<String> with names and the action-method just prints a "hello <name>" message to standard output.
This works fine. I get a list of names in the browser and a click fires the action-method that says hello to the specified name.
The Problem arises, when I want to put this code in a composite component that does the iteration and renders the actual link via a facet:
<ui:component xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:cc="http://xmlns.jcp.org/jsf/composite">
<cc:interface>
<cc:attribute name="value" type="java.util.List" required="true" />
<cc:facet name="content" />
</cc:interface>
<cc:implementation>
<ui:repeat value="#{cc.attrs.value}" var="_name">
<cc:renderFacet name="content"/>
</ui:repeat>
</cc:implementation>
</ui:component>
I use the composite component like this:
<my:myComp value="#{bean.names}">
<f:facet name="content">
<h:commandLink value="#{_name}" action="#{bean.sayHello(_name)}">
<f:ajax execute="#this"/>
</h:commandLink>
<br/>
</f:facet>
</my:myComp>
In the browser I get a list of names that looks exactly like before. But clicking a link now renders a "hello null" message. So _name is resolved correctly in the value attribute of <h:commandLink> but not in the action attribute.
I also tried using actionListener instead of action or the listener attribute from the <f:ajax> tag with no difference.
Could anybody shade some light on this issue?
My environment:
WildFly 8.1 with
JSF 2.2.6 (Mojarra)
The issue has to do with the scope of the variable in this case _name which is evaluated once when the <ui:repeat/> is being processed. In my case, I ran your code and it produced Hello John even though their were other names in my list. To get around this, I introduced a <f:param/> that would contain the value of the _name, and modified your code as follows:
<h:form>
<my:myComp value="#{bean.names}">
<f:facet name="content">
<h:commandLink value="#{_name}" action="#{bean.sayHello()}">
<f:param name="name_" value="#{_name}"/>
<f:ajax execute="#this"/>
</h:commandLink>
<br/>
</f:facet>
</my:myComp>
</h:form>
I also modified the sayHello() method as follows for a #RequestScoped bean:
#ManagedProperty(value = "#{facesContext}")
private FacesContext facesContext;
public void setFacesContext(FacesContext facesContext) {
this.facesContext = facesContext;
}
public void sayHello() {
Map<String, String> params = facesContext.getExternalContext()
.getRequestParameterMap();
String name = params.get("name_");
System.out.println("Hello " + name);
}
You could change this to something shorter in a #ViewScoped bean to:
public void sayHello() {
Map<String, String> params = FacesContext.getCurrentInstance()
.getExternalContext().getRequestParameterMap();
String name = params.get("name_");
System.out.println("Hello " + name);
}
The final result is that it prints out the names correctly.
I just started learning JSF and PrimeFaces, and I'm having a hard time trying to get PrimeFaces' datatable to work. I have a checkbox-based selection mechanism (more than one row can be selected at the same time), so that clicking on the "Delete selected users" button triggers a method call to the backing bean to delete the corresponding users from the database.
The problem is that the selected rows are not stored in the backing bean's selectedUsers array, and I can't understand why, since my code is exactly the same as the one hosted on PrimeFaces' ShowCase. As a consequence, the button successfully triggers the confirmation dialog, but no user ever gets deleted from the system. Does anyone have any advice?
Thank you in advance!
xhtml file
<h:form id="tableForm">
<p:dataTable id="userList" var="user" value="#{userListBean.userList}"
selection="#{userListBean.selectedUsers}" rowKey="#{user.username}">
<f:facet name="header">User list</f:facet>
<p:column selectionMode="multiple" style="width:2%"/>
<!-- data columns here, working just fine -->
<f:facet name="footer">
<p:commandButton value="Delete selected users" icon="ui-icon-trash" action="#{userListBean.deleteSelectedUsers()}">
<p:confirm header="Deletion confirmation" message="Are you sure you want to delete #{fn:length(userListBean.selectedUsers)} users?" icon="ui-icon-alert" />
</p:commandButton>
Registered users: #{fn:length(userListBean.userList)}
</f:facet>
</p:dataTable>
<p:confirmDialog global="true" showEffect="fade">
<p:commandButton value="Yes" type="button" styleClass="ui-confirmdialog-yes" icon="ui-icon-check" />
<p:commandButton value="No" type="button" styleClass="ui-confirmdialog-no" icon="ui-icon-close"/>
</p:confirmDialog>
</h:form>
Backing Bean
(userMgr handles persistence on the server, it should have nothing to do with my issue)
#ManagedBean(name="userListBean")
#ViewScoped
public class UserListBean {
#EJB
private UserManager userMgr;
private List<UserDTO> userList;
private UserDTO[] selectedUsers;
public UserListBean() {
}
public void deleteSelectedUsers() {
for(UserDTO u : selectedUsers)
userMgr.deleteUser(u.getUsername());
setUserList(userMgr.retrieveAllUsersDTO());
}
public List<UserDTO> getUserList() {
if(userList == null)
setUserList(userMgr.retrieveAllUsersDTO());
return userList;
}
public void setUserList(List<UserDTO> userList) {
this.userList = userList;
}
public List<UserDTO> retrieveUserList() {
return userMgr.retrieveAllUsersDTO();
}
public UserDTO[] getSelectedUsers() {
return selectedUsers;
}
public void setSelectedUsers(UserDTO[] selectedUsers) {
this.selectedUsers = selectedUsers;
}
}
(As a side question: the #{fn:length(userListBean.selectedUsers)} EL expression always returns 0. I used to think it was because of the broken selection mechanism, but could it be because the array is still empty when the confirmation dialog is rendered?)
You need to activate event rowSelect by adding <p:ajax event="rowSelect"/> to dataTable.
I have this code :
Add.xhtml :
<h:form id="nouv">
<h:panelGrid columns="2">
<h:outputText value="Nom:"></h:outputText>
<p:inputText value="#{ddeBean.nom}"></p:inputText>
<p:commandButton id="save"
value="ajouter"
update=":nouv:btn"
actionListener="#{ddeBean.ajouter}">
</p:commandButton>
<p:outputPanel id="btn">
<h:outputText rendered="#{ddeBean.created}" value="#{ddeBean.message}"/>
<p:commandButton id="btn_cr" value="add" rendered="#{ddeBean.created}"
action="pool.xhtml?faces-redirect=true">
</p:commandButton>
</p:outputPanel>
</h:panelGrid>
</h:form>
ddeBean.java :
#ManagedBean(name = "ddeBean")
#RequestScoped
public class DemandeBean implements Serializable{
ddeDAO dao = new ddeDaoImpl();
private String nom;
public String message = "";
private boolean created = false;
public void test(ActionEvent event){
Demande p = new Demande();
p.setDde(this.nom);
dao.Nouveau_dde(p);
created = true;
this.setMessage("saved!");
}
}
When I click commandButton Ajouter the message an the commandbutton Add will be shown, but commandbutton Add doesn't redirect to pool.xhtml.
When you click on the button Add, the bean is recreated and the value #{ddeBean.created} then reinitialized to false so the button doesn't get rendered before action take place.
To fix that, you need to change the scope of your bean to #ViewScoped.
You should also make sure to replace
public void test(ActionEvent event){
by
public void ajouter(ActionEvent event){
if you want that your button work properly.
Another solution
Since you are using PrimeFaces, you could show your button by JavaScript :
<p:commandButton id="save"
value="ajouter"
oncomplete="panel-create.show();"
actionListener="#{ddeBean.ajouter}">
</p:commandButton>
<p:outputPanel id="btn" style="display: none;" widgetVar="panel-create">
<h:outputText value="#{ddeBean.message}"/>
<p:commandButton id="btn_cr" value="add"
action="pool.xhtml?faces-redirect=true">
</p:commandButton>
</p:outputPanel>
Note the oncomplete attribute on the p:commandButton, the widgetVar attribute on the p:panel and the rendered attributes removed.
I have following sample code.
Initially, only commandButton Two is visible. When I click this button, commandButton One is also visible. But when I click One, the backing-bean method click1 does not get fired.
Following is my code:
xhtml
<h:form id="form1">
<h:inputHidden id="show" value="#{bean.show1}" />
<h:commandButton id="button1" value="One" action="#{bean.click1}"
rendered="#{bean.show1}" />
</h:form>
<h:form id="form2">
<h:inputHidden id="show" value="#{bean.show1}" />
<h:commandButton id="button2" value="Two" action="#{bean.click2}" />
</h:form>
backing-bean
#RequestScoped
#Named("bean")
public class JsfTrial implements Serializable {
private static final long serialVersionUID = 2784462583813130092L;
private boolean show1; // + getter, setter
public String click1() {
System.out.println("Click1()");
return null;
}
public String click2() {
System.out.println("Click2()");
setShow1(true);
return null;
}
}
I found a very informative answer by BalusC.
h:commandLink / h:commandButton is not being invoked
If I understand it correctly, my problem is due to point 5 of this answer.
Does that also mean we can not use hidden commandButton with #RequestScoped backing-bean?
You can use the request scope, you should only pass the condition as a request parameter to the subsequent requests by <f:param> instead of by a JSF hidden input field <h:inputHidden>. The value of the hidden input field is only set in the model during Update Model Values phase, while the condition of the rendered attribute is already evaluated during Apply Request Values phase which is earlier.
So, use <f:param> instead of <h:inputHidden>:
<h:form id="form1">
<h:commandButton id="button1" value="One" action="#{bean.click1}"
rendered="#{bean.show1}">
<f:param name="show1" value="#{bean.show1}" />
</h:commandButton>
</h:form>
<h:form id="form2">
<h:commandButton id="button2" value="Two" action="#{bean.click2}">
<f:param name="show1" value="#{bean.show1}" />
</h:commandButton>
</h:form>
This way you can extract them as request parameter in bean's (post)constructor.
public JsfTrial() {
String show1 = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("show1");
this.show1 = (show1 != null) && Boolean.valueOf(show1);
}
Ugly, but CDI doesn't offer a builtin annotation which substituties JSF's #ManagedProperty("#{param.show1}"). You could however homegrow such an annotation.
Usually i do something like below. Clicking button execute transition.
<!-- view -->
<h:form>
<h:commandButton action="doit">
<f:ajax render="#form"/>
</h:commandButton>
</h:form>
<!-- flow -->
<transition on="doit">...</transition>
How to fire a transition on change value in (for example) h:selectOneMenu ?
<h:form>
<h:selectOneMenu value="#{selected}">
<f:selectItems value="#{items}/>
<f:ajax event="valueChange" render="#form" />
</h:selectOneMenu>
</h:form>
Edit:
I thought about registering listener to f:ajax and prepare webflow event, but how to use that event... ? Anybody help ?
<h:form>
<h:selectOneMenu value="#{selected}">
<f:selectItems value="#{items}/>
<f:ajax event="valueChange" render="#form" listener="#{bean.changeListener}" />
</h:selectOneMenu>
</h:form>
java:
import javax.faces.event.AjaxBehaviorEvent;
import org.springframework.webflow.execution.Event;
public class Bean {
public void changeListener(AjaxBehaviorEvent event) {
// prepare webflow event
Event e = new Event(event.getSource(), "doit");
// propagate this event... ???
}
}
I recently had a similar issue, and handle primefaces/richfaces events with a similar listener style. Here's an example:
public void changeListener(AjaxBehaviorEvent event) {
RequestContext requestContext = RequestContextHolder.getRequestContext();
RequestControlContext rec = (RequestControlContext) requestContext;
//place variables you need in next flow phase here; flash,application,session scope
rec.getFlashScope().put("someVarIneedInNextFlow", varName);
rec.handleEvent(new Event(this, "flow transition name here, i.e. next-stage"));
return;
}
That should transition to whichever flow event you desire :)