How to change the value in h:commandlink onclick - jsf

I need to implement h:commandlink in my project. I need to change the value "Follow" to "Following" in the commandlink once the user clicks it. How do i do that? Can someone help me with this? Thanks for your time and help in advance.
<h:commandlink id="followdoc" action="Usermanger.followdoctor" value="Follow" />
Usermanager is my jsf managed bean class and followdoctor is a method in bean class

If you want to do this at the client side, just grab JavaScript. After all, JSF ends up as plain HTML in webbrowser (rightclick page in browser and choose View Source, you'll understand what I mean).
<h:commandLink value="Follow" action="#{bean.followDoctor}" onclick="this.innerHTML='Following'" />
But if you want to change (and if necessary memorize/persist) this in the server side, then just make it a bean property so that you can change it in the action method.
<h:commandLink value="#{bean.linkValue}" action="#{bean.followDoctor}" />
with
private String linkValue;
public Bean() {
this.linkValue = "Follow"; // Preinitialize it somehow.
}
public String followDoctor() {
this.linkValue = "Following";
// ...
}

Related

p:button with onclick javascript hook

I'm using p:button with onclick action. (I can't move to p:commandButton, because of legacy outcome navigation in faces-config.xml and custom navigation history in db):
<p:remoteCommand name="unlock_tt" actionListener="#{ttEntityMBean.unlock()}"/>
<p:button value="#{msgs['button.ok']}" outcome="#{ttEntityMBean.navigationMenuItemToRedirect.navigationRule}" onclick="unlock_tt()"/>
Generated by primefaces javascript looks like
onclick="unlock_tt(); window.open(....)"
And after clicking button, unlock_tt() in browser initiated, but immediatly broken by page redirecting, so backed java method didn't execute.
Should I make unlock_tt() or java call async to be sure it will be executed before browser leaves page?
Upd: I'm thinking to use p:commandButton, if it is possible to get to-view-id programically, like in this question:
Programmatically get navigation case <to-view-id> from faces-config.xml by outcome
<p:commandButton action="#{ttEntityBean.unlock()}"/>
public String unlock() {
//some business logic
return OutcomeResolverHelper.getRuleFor(navigationMenuItemToRedirect.navigationRule)
}
This should reduce number of requests
I'm not sure this will work but can you try this :
Instead of :
<p:remoteCommand name="unlock_tt" actionListener="#{ttEntityMBean.unlock()}"/>
<p:button value="#{msgs['button.ok']}" outcome="#{ttEntityMBean.navigationMenuItemToRedirect.navigationRule}" onclick="unlock_tt()"/>
do:
<h:panelGroup>
<f:ajax event="click" listener="#{ttEntityMBean.unlock}"/>
<p:button value="#{msgs['button.ok']}" outcome="#{ttEntityMBean.navigationMenuItemToRedirect.navigationRule}" />
<h:panelGroup>
Don't forget to remove onclick and your remotecommand.
This should wrap a panelGroup around your button. PanelGroup implements ClientBehaviorHolder so it can get f:ajax tag in its children. Thus when you click on it it should trigger the listener.
The easiest way was migration from p:button to p:commandButton, which allows to execute some java-code in action method before redirect. The core is programmical translation of navigation rule, defined in faces-config.xml to base url, usable in explisit jsf-2.0 style:
//receive base url from <to-view-id> tag
public static String resolveOutcomeRule(String ruleName) {
FacesContext context = FacesContext.getCurrentInstance();
ConfigurableNavigationHandler configNavHandler = (ConfigurableNavigationHandler)context.getApplication().getNavigationHandler();
NavigationCase navCase = configNavHandler.getNavigationCase(context, null, ruleName);
return navCase.getToViewId(context);
}
//decorate it with faces-redirect=true and our custom navigation
public static String resolveOutcome(NavigationMenuItem item) {
return resolveOutcomeRule(item.getNavigationRule()) + "?faces-redirect=true&menu=" + item.getId();
}
//controller-method
public String cancelEditTT() {
super.unlock();
return FacesUtil.resolveOutcome(getNavigationMenuItemToRedirect());
}
//new jsf button:
<p:commandButton value="#{msgs['button.ok']}" action="#{ttEntityMBean.cancelEditTT}"/>
This solution provided 1-step redirect instead of 2-step old one.

jsf 2 outputlink rendering method call without clicking

I am relatively new in jsf.
I have a jsf page someDetails.xhtml, in which I have an output link
<h:outputLink value="#{someTaskController.completeTask(taskId)}?taskId=#{taskId}">Assign Ticket</h:outputLink>
On clicking on this link, the method completeTask should be called and do something.
The problem is, when the jsf page someDetails.xhtml is opened(in browser), the method completeTask is getting called and does all the task, which should only happen on clicking the link.
What should I do ? Please help
You're using the wrong tag for your purpose, use command link instead:
<h:commandLink value="Assign Ticket" action="#{someTaskController.completeTask()}">
<f:param name="taskId" value="#{taskId}" />
</h:commandLink>
You can access taskId inside method completeTask() like this:
public void completeTask() {
Map<String,String> params =
FacesContext.getExternalContext().getRequestParameterMap();
String taskId= params.get("taskId");
// do your business action...
}

Primefaces won't call setter on ManagedBean

I'm in the middle of building a web application (Spring 4, Primefaces 5.1 and JPA).
While one of my xhtml-pages has no problems working with its ManagedBean, the others won't really work. To be more specific, the UI input components (like inputText) won't call any setters from the managed Beans.
I've already run my Tomcat (8) on debug mode, which showed me that the page calls the get-methods, but no set-methods at all. If I try to persist something, all values are null (or 0, in case of an int-value). (it even persists an object into the database with all values null, though I have declared some #NotNull constraints which should be taken to the database configuration via JPA)
So, question is: How can I make my inputFields work with the fields of my ManagedBean? (Eclipse also shows me in the editor, that theoretically, the connection to the fields is there, it knows the managed bean, the field and the get-/set-methods)
SoftwareManagedBean.java
#ManagedBean(name = "swmb")
#ViewScoped
public class SoftwareManagedBean extends AssetManagedBean implements
Serializable {
private String bezeichnung;
private Software neueSoftware;
// +some more private fields, every single one with its get-/set-method
#Override
public String getBezeichnung() {
return super.getBezeichnung();
}
#Override
public void setBezeichnung(final String bezeichnung) {
super.setBezeichnung(bezeichnung);
}
//instantiante the field "neueSoftware"
public void createEmptySoftware(){
if(neueSoftware != null)
return;
this.neueSoftware = new Software();
}
//Persist Software with values from inputFields
public void addSoftware() {
createEmptySoftware();
neueSoftware.setBezeichnung(getBezeichnung());
softwareService.addSoftware(neueSoftware);
//...
neueSoftware = null;
}
viewSoftware.xhtml
<h:body>
<p:dialog header="Neue Software anlegen" widgetVar="SwNeuDialog" width="60%"
closeOnEscape="true" draggable="false" resizable="false" position="center">
<h:form id="SwDlgForm">
<h:panelGrid columns="3" border="0" >
<p:outputLabel for="swBezeichnung" value="Bezeichnung: " />
<p:inputText id="swBezeichnung" value="#{swmb.bezeichnung}"
label="Bezeichnung" required="true" />
<f:verbatim/>
<p:outputLabel for="swKategorie" value="Kategorie: " />
<p:selectOneMenu id="swKategorie" value="#{swmb.kategorie}" label="Kategorie" required="true" >
<f:selectItem itemLabel="Kategorie wählen" value="#{null}" noSelectionOption="true"/>
<f:selectItems value="#{swmb.kategorieListe}" var="kat" itemLabel="#{kat.bezeichnung}" itemValue="#{kat}"/>
</p:selectOneMenu>
<p:commandButton value="neue Kategorie hinzufügen" />
<!-- + some more input fields -->
<p:commandButton value="Speichern" action="#{swmb.addSoftware()}" onclick="PF('SwNeuDialog').hide()" resetValues="true" process="#this"/>
<p:commandButton value="Abbrechen" onclick="PF('SwNeuDialog').hide()" resetValues="true" process="#this"/>
</h:panelGrid>
</h:form>
</p:dialog>
<!-- dataTable -->
</h:body>
AssetManagedBean.java
#ManagedBean
public abstract class AssetManagedBean {
//name of the hard-/software
private String bezeichnung;
//+ some more fields with get-/set methods
public String getBezeichnung() {
return bezeichnung;
}
public void setBezeichnung(String bezeichnung) {
this.bezeichnung = bezeichnung;
}
I hope that code is sufficient to see the problem, since the rest of the code follows the same structure. I think, the problem could lie within the xhtml file, but I don't see where or why. I've got the SpringBeanFacesELResolver (or however it is called), I've already looked through the code and compared it to another xhtml page and its Managed Bean, but there are no differences anymore. (though one is working, one not)
My debugger showed, how the working class/page (viewAdministration.xhtml) called the get-/set methods of the managed bean:
opening dialog window: get...(), set...()
clicking the commandButton to submit/persist: get() (old Value), set() (new Value), get() (new Value)
Another get() (called by the add... method)
(+ another get() for the dataTable on the main page)
on viewSoftware.xhtml, it looks like this:
opening dialog window: get()
clicking the commandButton to submit/persist:
another get() called by the addSoftware method
As you can see, when I try to submit, there is no set or get.
So, to summarize:
no setter called by trying to submit
the code on viewSoftware.xhtml and SoftwareManagedBean is similar to another, functioning ManagedBean + xhtml page (I've compared it again and again)
annotations in Managed Beans are the same (#ManagedBean, #ViewScoped)
the inputFields are inside a form (
I'm totally clueless, but I think it is some small mistake from my side that I can't just see.
I've searched through the web and especially stackoverflow, but all the problems and answers I've found couldn't help me finding what's wrong
Even without inheriting from a superclass it won't work (tried that out too)
I hope, you can help me. If this post is lacking some information, I'm sorry about that, I tried my best to not let this post grow too big and still get as much relevant information in it as possible.
So, I have found my mistake (or at least, I think I have). Only took me 2 weeks, but anyway...
I tried to test it out more specifically, wrote test classes and an xhtml page. Nothing worked (from simple Input over Dates to own classes).
The solution to this problem was to disable ajax on my commandButton (ajax="false"). When I tried to understand more of it, I realized that the commandButton opening the dialog window was nested in a facet inside a dataTable, and thus it had problems to properly set the values of the input fields.
So, thank you for your help. Maybe/hopefully this can or will help some other people later as well.
From the first glance at the code, without reading it whole, try putting process="SwDlgForm" on your command buttons, instead of process="#this". If that doesn't solve the problem, I'll read more carefully and try to help.

Navigate after successfull action on commandButton

I am building a JSF application using Primefaces mobile (0.9.3 with Primefaces 3.5).
I hava a login page, on this login page there is a login button defined as followed:
<p:commandButton value="#{msg['label.login']}" action="#{loginBean.login}" update="usernameMsg" />
The loginBean.login method is defined as followed:
public String login() {
try {
//do login
return "#loggedInTarget?transition=fade";
} catch (UserNotValidException e) {
//User not valid, display exception
FacesContext
.getCurrentInstance()
.addMessage(
"loginForm:username",
new FacesMessage(
FacesMessage.SEVERITY_ERROR,
"",
messageService
.getMessage("error.message.loginNotSuccessfull")));
}
return null;
}
In my main xhtml (the same in which the button is defined), I have defined the following addtional views:
<pm:view id="registerTarget">
<ui:include
src="/WEB-INF/c52bc19d58a64ae4bd12dec187a2d4b7/register.xhtml" />
</pm:view>
<pm:view id="loggedInTarget">
TEST
</pm:view>
I do it that way because I want to use transitions within Primefaces mobile. To get to the register page I use a p:button which works fine:
<p:button value="#{msg['label.register']}"
href="#registerTarget?transition=fade" />
Why is my login button not working? I also tried to put in "#loggedInTarget?transition=fade" directly as action, but this didn't work either :(
I need the action, because I have to check if the user credentials are valid. If they are valid I want to display my loggedInTarget.
How can I achieve it?
Thanks in advance!
EDIT:
The button is inside a form.
The navigation case outcome value must represent a view ID, not an URL (for sure not a hash fragment). Try with <p:button outcome="...">, and you'll see that it fails over there as well.
Your closest bet is sending a redirect.
public void login() throws IOException {
// ...
externalContext.redirect("#loggedInTarget?transition=fade");
// ...
}
This is also exactly what the <p:button href="..."> does under the covers: causing a GET request on the given URL.

h:commandButton action redirect to context root, possible?

Project context
I've created from scratch an URL rewriting with two major component :
public class URLFilter implements Filter
{
...
}
public class URLViewHandler extends GlobalResourcesViewHandler
{
...
}
The first class is used to forward clean URLs to the right view with an ID different for each page. The second class override the function getActionURL() so that h:form and ajax functionnalities continues to work.
Theses classes translate like this :
Real URL Internal URL
/ <-> page.jspx?key=1
/contact <-> page.jspx?key=2
/projects/management <-> page.jspx?key=3
etc
Current solution
My problem right now is for my user login and logout button :
<!-- Login button used if user is not logged, go to a secured page (which display error message). If he log with this button, the current page is reloaded and displayed properly. This button works perfectly -->
<h:commandButton rendered="#{pageActions.item.isPrivate}" value="#{msg.button_connect}" actionListener="#{userActions.onButtonLoginClick}" />
<!-- Login button used anywhere on public pages that redirect to user home after login, works perfectly since I haven't changed to clear url. -->
<h:commandButton rendered="#{not pageActions.item.isPrivate}" value="#{msg.button_connect}" actionListener="#{userActions.onButtonLoginClick}" action="userHome.jspx?faces-redirect=true" />
<!-- Logout button that works (it redirects at http://website.com/context-name/ but keep the ?key=1 at the end. -->
<h:commandButton value="#{msg.button_disconnect}" actionListener="#{userActions.onButtonLogoutClick}" action="page.jspx?key=1&faces-redirect=true" styleClass="button" style="margin-left: 5px;" />
My whishes
My question : Is there a better way to program the logout button since I need to redirect to context-root, currently I'm using the view name with the home page key but I would prefer 1. using a real path 2. not keep the ?key=1 at the url.
Thank you!
Final code
Based on BalusC answer, here is my final code to share to others :
#ManagedBean
#RequestScoped
public class NavigationActions
{
public void redirectTo(String p_sPath) throws IOException
{
ExternalContext oContext = FacesContext.getCurrentInstance().getExternalContext();
oContext.redirect(oContext.getRequestContextPath() + p_sPath);
}
}
<h:commandButton rendered="#{not pageActions.item.isPrivate}" value="#{msg.button_connect}" actionListener="#{userActions.onButtonLoginClick}" action="#{navigationActions.redirectTo(userSession.language.code eq 'fr' ? '/profil/accueil' : '/profile/home')}" />
Since I don't need the key when I have the path, it is even more better, thank you again BalusC to put me on the right track! Sent a small donation :)
This isn't possible with (implicit) navigation. The / is unfortunately not a valid JSF view ID.
Use ExternalContext#redirect() instead. Replace
action="page.jspx?key=1&faces-redirect=true"
by
action="#{userActions.redirectToRootWithKey(1)}"
with
public void redirectToRootWithKey(int key) throws IOException {
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
ec.redirect(ec.getRequestContextPath() + "?key=" + key);
}

Resources