0

After days of googling I must give up and ask for help. I have xhtml page with jsf tags. Whole page is controlled by PageController.java. Important thing is that controller takes item Id and page number from url parameters. Beside that I have small form which contains current logged user information or username and password input if no one is logged in. I would like have possibility to log in or log out user using that form. But when I call either user logIn() or logOut() function from user controller parameters from url just missing. I've already tried using h:commandbutton and h:commandlink as well. I managed to send parameters via f:param tag but they still missing from url (that's problem because I need bookmarking here). tags like h:link, h:button or h:outputLink cannot be used because they don't allow me to call controller function on click. Thanks for any help.

Edit: I forgot to mention, I've tried to solve this via f:ajax. Problem is after log in I cannot log out without refreshing page. This same with other direction.

Edit II: I will show sample code illustrating the problem. In my project I have template.html which is simple page template containing header, footer, aside menu, and main content. It probably have not influence at the problem I've met. My code looks like that:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core">
<f:metadata>
<f:event type="preRenderComponent" listener="#{PageController.init()}"/>
</f:metadata>   
<ui:composition template="/template.xhtml">  
<ui:define name="aside-menu">
<h:form id = "sidemenuform">                   
<h:panelGrid rendered="#{usersController.isLoggedIn()}" columns="1">
<h:commandLink value="LogOut" >
                    <f:param name="orderId" value="${PageController.groupId}"/>
                    <f:param name="page" value="${PageController.PageNumber}"/>
                </h:commandLink>
            </h:panelGrid>
            <h:panelGrid rendered="#{!usersController.isLoggedIn()}" columns="1">                     
                <h:outputLabel for="usernameInput" value="#{bundle.LogginUsernameLabel}: "/>
                <h:inputText id="usernameInput" value="#{usersController.username}"/>
                <h:outputLabel for="passwordInput" value="#{bundle.LogginPasswordLabel}:">
                </h:outputLabel>
                <h:inputSecret id="passwordInput" value="#{usersController.password}"/>
                <h:commandButton action="#{usersController.login()}">
                    <f:param name="orderId" value="${PageController.GroupNumber}"/>
                    <f:param name="page" value="${PageController.PageNumber}"/>
                </h:commandButton>
            </h:panelGrid>                  
        </h:form>
    </ui:define>
    <ui:define name="body">
        <div id="navBar">
            <ul>
                        <li class="previousPage">    
                            <h:commandLink action="#{PageController.previous}"  rendered="#{PageController.pagination.hasPreviousPage}">
                            </h:commandLink>                    
                        </li>
                        <li class="nextPage">    
                            <h:commandLink action="#{PageController.next}"  rendered="#{PageController.pagination.hasNextPage}">
                            </h:commandLink>        
                        </li>   
                    </ul>
        </div>
        <ui:repeat value="#{PageController.items}" var="item" varStatus="status">
            <!-- List of items in group divided into pages -->
        </ui:repeat>
    </ui:define>
</ui:composition>

PageController.previous and PageController.next functions working well because they returning url with GET parameters as string which looks like:

Page?groupId=1&PageNumber=2&faces-redirect=true

But userController knows nothing about them, neither about page which we currently show. So functions Login and Logout can not return url. Both functions return void. The problem is that when I fire these functions after page reload my address url changed from

/Project/faces/groups/Page.xhtml?groupId=534?pageNumber=1

To

/Project/faces/groups/Page.xhtml

And then init function fail. How to prevent this behavior??

user2800697
  • 27
  • 1
  • 6
  • Could you provide some of your code? It seems you try to mention lot of problems in a single question... – Aritz Aug 04 '14 at 13:46
  • Is there any way to call java function onclick without using commandbutton or commandlink?? for example using h:link or h:button? – user2800697 Aug 05 '14 at 09:00
  • Which is the scope of your page and user controllers? The behaviour for the `Login` method is the expected one if you return `void`... – Aritz Aug 05 '14 at 09:56
  • PageController is Session scope (Should be request anyway) and UserController is Session as well. I've tried return void and empty string. I know that will set view to current page. I rather tried to pass GET parameter via f:param tag attached to commandbutton/commandlink. Neither of this works for me. h:link and h:button works fine as expected but I can't call login and logout function on click. – user2800697 Aug 05 '14 at 10:07

2 Answers2

1

h:link and h:button create javascript links, there's no chance to use them for invoking server side methods. What you need is to POST the server with user credentials and later on invoke a REDIRECT if the login process is correct. Your best is to use a page controller for the current view and a user controller which has the session info for the logged user. You can easily inject one bean into the other one:

@ManagedBean
@SessionScoped
public class UserController{

    public boolean doLogin(String email, String password){
        //Do your internal request here
    }

}

@ManagedBean
@ViewScoped
public class PageController{

    //You'll need a setter for this
    @ManagedProperty(value=#{userController})
    private UserController userController;

    public String doLogin(){
        if (userController.doLogin(email, password)){
            return "page?groupId=" + groupId + "PageNumber=" + 
                pageNumber +"faces-redirect=true";
        }
        else{
            //Add some faces message describing the error
        }
    }

}

That way you make the process transparent for the view, which will only acess page controller methods.

Aritz
  • 30,971
  • 16
  • 136
  • 217
  • I considered something like that, but thought it's too messy... Seems like I was wrong. I will try that way:) – user2800697 Aug 05 '14 at 10:23
  • I think it's much better than accessing both of the controllers in the same view, don't you think? – Aritz Aug 05 '14 at 10:31
  • I must confess that I'm really new to jsf and java. I read that it depends from your needs how many controllers you used in your view. I also thought is good Idea because I can put this very code on every page I need and don't have to implement this same wrap functions in every controllers for those pages. – user2800697 Aug 05 '14 at 10:49
  • That's the way I do it. Anyway, it shouldn't be a problem performing implicit navigation from a session scoped bean as you do. You could do the way you are doing right now, just passing the parameters though the view: `#{usersController.login(orderId,page)}`, supposing EL 2 is available. That however involves mixing such a view related params in the user controller... – Aritz Aug 05 '14 at 11:01
  • Ok, I wrapped login and logout functions in pageController. To simplify I get user controller from current context (Managed property of course is way better). It works indeed. Still quite not convinced, but thanks a lot. I will play with this code for a while yet:) – user2800697 Aug 05 '14 at 11:13
0

Try using f:viewParams inside your f:metadata tags:

<f:metadata>
    <f:viewParam name="orderId" value="${PageController.groupId}"/>
    <f:viewParam name="page" value="${PageController.PageNumber}"/>
</f:metadata>

and append ?includeViewParams=true to the url returned by userController.login() and userController.logout(). This should append the view parameters to the url automatically.

cghislai
  • 1,751
  • 15
  • 29
  • While login and logout functions don't return any url i put there code: return "?includeViewParams=true"; this cause following error: Unable to find matching navigation case with from-view-id '/groups/Page.xhtml' for action '#{usersController.login()}' with outcome '?includeViewParams=true' – user2800697 Aug 05 '14 at 08:21
  • try returning "#?includeViewParams=true". – cghislai Aug 05 '14 at 08:31
  • Same error message. even when: return getCurrentPageUri() + "?includeViewParams=true"; And: private String getCurrentPageUri(){ String s = ((HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest()).getQueryString(); return ((HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest()).getRequestURI(); } – user2800697 Aug 05 '14 at 08:41
  • In addition I have error The metadata component needs to be nested within a f:metadata tag. Suggestion: enclose the necessary components within for code: – user2800697 Aug 05 '14 at 09:06
  • Indeed returning "#?includeViewParams" does not work as expected when some attributes are present, sorry for the wrong advice. If your action does not need any navigation case, you can use ajax to call it, either by using , or nesting a inside a for instance. In both case, the url should not be touched. If you need to update the page afterwards, i would use . – cghislai Aug 05 '14 at 09:22
  • For the metadata warning, see http://stackoverflow.com/questions/17945943/the-metadata-component-needs-to-be-nested-within-a-fmetadata-tag-suggestion-e – cghislai Aug 05 '14 at 09:24
  • Ok, I put f:ajax tag into commandlink with parameter render="sidemenuform". Works fine... nearly. Because after one action I must click twice to call other. e.g After login, if I want to logout, I must click logout link twice instead of once... I really starting to hate jsf... I had this solution earlier and face same issue with it. In addition render="@all" just rip off my whole css so I had nothing but white background with text... – user2800697 Aug 05 '14 at 09:41