mist
mist

Reputation: 1853

h:outputLink with f:ajax - method called, but link not shown

This does not work

<h:form style="display: inline;">
    <h:outputLink value="#{title.link}" >
        #{msg['g.readMore']}
        <f:ajax event="click" immediate="true" listener="#{titlesBean.titleClicked(title.id)}" />
    </h:outputLink>
</h:form>

What I want it to do is when clicked to call #{titlesBean.titleClicked(title.id)} and then go to the link. The method is called but it doesn't go to the link. There are several other ways to do it (with commandLink and then a redirect, but I would like to know why this is not working).

This is the method itself:

public String titleClicked(long titleId) {
    this.titlesManager.addXtoRating(titleId, 1);
    return null;
}

Note: this is only a sidenote, but I accidentally found out that this works:

<script type="text/javascript">
    $('.share').popupWindow({centerBrowser:1,height:380,width:550});
</script>

<h:form style="display: inline;">
    <h:outputLink styleClass="share" value="http://www.facebook.com/sharer.php...">
        <img src="images/facebook-icon.jpg" />
        <f:ajax event="click" immediate="true" listener="#{titlesBean.titleLiked(title.id)}" />
    </h:outputLink>
 </h:form>

Check out the styleClass="share"

Update: (I have less than 100 rep, so I cannot answer my own question for 8 hours, this is how to put it delicately - stupid). I waited for a while, but nobody answered.

So this is my hacked solution ( I don't like it at all, but it works):

<h:form style="display: inline;">
    <h:outputLink target="_blank" styleClass="click8" value="#{title.link}" >
        #{title.heading}
        <f:ajax event="click" immediate="true" listener="#{titlesBean.titleLiked(title.id)}" />
    </h:outputLink>
</h:form>

And this is the important part:

<h:head>
    <script type="text/javascript">
        $(document).ready(function(){
            $('.click8').click(function (event){
                         var url = $(this).attr("href");
                         window.open(url, "_blank");
                         event.preventDefault();
            });
        });
    </script>
</h:head>

Note: this has to be in the header, otherwise I had a major bug with the link opening a thousand windows.

Upvotes: 3

Views: 6517

Answers (1)

BalusC
BalusC

Reputation: 1108722

It does not work because there's means of a race condition here. Two HTTP requests are been sent simultaneously in the same window. One ajax to the server and one normal to the given link. The one which finishes sooner wins. You want to send the HTTP requests in sync. First the ajax one to the server and when it returns, then the normal one to the given link.

As to your hacky solution, it works because it uses JavaScript to open the URL in a new window instead of the current one and then blocks the link's default action, so the normal response just arrives in a completely separate window while the ajax response still arrives in the initial window. So there's no means of a race condition of two HTTP requests in the initial window anymore.

As to the final solution, this is not possible with standard set of JSF 2.0 components. Using <h:commandLink> and then doing a redirect is indeed doable, but the link is this way not crawlable by searchbots and it fires effectively a POST request, which is IMO more worse than your new window solution.

If you would really like to open the link in the current window, hereby keeping the target URL in the link's href, then I'd suggest to create a simple servlet which does the link tracking and redirecting job and let jQuery manipulate the link target during onclick.

Something like this

<a rel="ext" id="ext_#{title.id}" href="#{title.link}">read more</a>

(HTML element IDs may not start with a digit! Hence the ext_ prefix, you can of course change this whatever way you want.)

with

$(function() {
    jQuery('a[rel=ext]').click(function(e) {
        var link = jQuery(this);
        var url = 'track'
            + '?id=' + encodeURIComponent(link.attr('id')) 
            + '&url=' + encodeURIComponent(link.attr('href'));

        if (link.attr('target') == '_blank') {
            window.open(url);
        } else {
            window.location = url;
        }

        e.preventDefault();
    });
});

and

@WebServlet(urlPatterns={"/track"})
public class TrackServlet extends HttpServlet {

    @EJB
    private TrackService trackService;

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String id = request.getParameter("id");
        String url = request.getParameter("url");

        if (id == null || url == null) {
            response.sendError(HttpServletResponse.SC_BAD_REQUEST);
            return;
        }

        trackService.track(id.replaceAll("\\D+", "")); // Strips non-digits.
        response.sendRedirect(url);
    }

}

Upvotes: 4

Related Questions