jsf 2 outputlink rendering method call without clicking - jsf

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...
}

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.

How to count the number of times a h:outputLink was clicked?

I have a PrimeFaces page with following code:
<pm:content id="content">
<p:dataList value="#{likeditems.likedItems}" var="item" pt:data-inset="true" paginator="true" rows="5">
<f:facet name="header">
Products you liked in the past
</f:facet>
<h:outputLink value="#{item.url}" target="_new">
<p:graphicImage name="http://example.com/my-product-mobile/f/op/img/underConstructionImage.jpg" />
<h2>#{item.title}</h2>
<p>Approx. #{item.price} (for most up-to-date price, click on this row and view the vendor's page)</p>
</h:outputLink>
<f:facet name="footer">
Products you liked in the past
</f:facet>
</p:dataList>
</pm:content>
When the user clicks on the h:outputLink, I want 2 things to happen:
A new page with URL item.url is opened in the browser.
Method likeditems.itemLinkClicked(item) is invoked (in that method I update the number of times a particular link was clicked).
First thing is already working (target="_new").
How can I implement the second one (method call for updating the number of times the link was clicked) without the first ceasing to work?
First thing is already working (target="_new").
The target should actually be _blank.
How can I implement the second one (method call for updating the number of times the link was clicked) without the first ceasing to work?
The simplest (naive) JSF-ish way would be triggering a <p:remoteCommand> on click.
<h:outputLink value="#{item.url}" target="_blank" onclick="count_#{item.id}()">
...
</h:outputLink>
<p:remoteCommand name="count_#{item.id}" action="#{likeditems.itemLinkClicked(item)}" />
But this generates lot of duplicate JS code which is not very effective. You could put it outside the data list and fiddle with function arguments. But this still won't work when the enduser rightclicks and chooses a context menu item (open in new tab, new window, new incognito window, save as, copy address, etc). This also won't work when the enduser middleclicks (default browser behavior of middleclick is "open in a new window").
At ZEEF we're using a script which changes the <a href> on click, middleclick or rightclick to an URL which invokes a servlet which updates the count and then does a window.open() on the given URL.
Given a
<h:outputLink value="#{item.url}" styleClass="tracked" target="_blank">
the relevant script should basically look like this:
// Normal click.
$(document).on("click", "a.tracked", function(event) {
var $link = $(this);
updateTrackedLink($link);
var trackingURL = $link.attr("href");
$link.attr("href", $link.data("href"));
$link.removeData("href");
window.open(trackingURL);
event.preventDefault();
});
// Middle click.
$(document).on("mouseup", "a.tracked", function(event) {
if (event.which == 2) {
updateTrackedLink($(this));
}
});
// Right click.
$(document).on("contextmenu", "a.tracked", function(event) {
updateTrackedLink($(this));
});
// Update link href to one of click count servlet, if necessary.
function updateTrackedLink($link) {
if ($link.data("href") == null) {
var url = $link.attr("href");
$link.data("href", url);
$link.attr("href", "/click?url=" + encodeURIComponent(url));
}
}
and the click servlet should look like this (request parameter validation omitted for brevity):
#WebServlet("/click")
public class ClickServlet extends HttpServlet {
#EJB
private ClickService clickService;
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String url = request.getParameter("url");
clickService.incrementClickCount(url);
response.sendRedirect(url);
}
}
Note that this way the target="_blank" isn't necessary. It would only be used by users who have JavaScript disabled. But then many more other things on the website wouldn't work anyway, including the above JS tracking. It this is also really your concern, then you'd better just put that click servlet URL directly in <h:outputLink value>.

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.

how to redirect from a jsf facelet if an el expression is true?

I have a jsf facelet which displays a summary of a search process.
Now I want to redirect to a detail page, if only one element was found by the search engine. I do not want to implement this redirect in my beans because I want to have the "knowledge" about this redirection out of my java code.
So I want to write something like that:
<ui:redirect if="#{searchResult.count eq 1}" target="details.jsf">
<f:param name="id" value="#{searchResult.firstResult.technicalId}" />
</ui:redirect>
Any solutions or ideas for that?
I know, there are page-actions in JSF 2.2 but I am using JEE6 and there is JSF 2.0 available.
Btw. I currently I am usingMyFaces, Primefaces and Richfaces.
Greetings.
You should do this job in the controller, not in the view.
To get search results, you first need to invoke an action method by a search form, right? Just do the redirect job right there. You can tell JSF to send a redirect by specifying the faces-redirect=true in the query string of the action outcome.
public String search() {
results = service.find(query);
if (results.size() == 1) {
return "details?faces-redirect=true&id=" + results.get(0).getTechnicalId();
}
else {
return "results";
}
}
Or if it's a GET request which is handled by <f:event type="preRenderView">, then do so:
public void onPreRenderViewListener() {
results = service.find(query);
if (results.size() == 1) {
FacesContext.getCurrentInstance().getExternalContext().redirect("details.jsf?id=" + results.get(0).getTechnicalId());
}
}
I would say the cleanest way is to generate the action in the bean.
Anyway I would propose you to simulate a click in case your search count is eq to 1 with javascript.
you need an input hidden containing the count:
<h:inputHidden id="count" value="#{searchResult.count}" />
and a hidden button to trigger the redirection:
<h:commandButton id="redirectButton" action="details.jsf" style="display: none;" />
then when you trigger the search, you update the input count and oncomplete you can check
if the value of count is 1, then you can do a click on the commandButton.
If you use Jquery, it would be something like
if($("#count").val()==1){
$("#redirectButton").click();
}

How to change the value in h:commandlink onclick

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";
// ...
}

Resources