-1

i have a JSF projekt and in there i have different views, which are backed by ManagedBeans.

What i would like to achieve is to change some views while others stay where they are. this has to happen dynamically. In other words. I want to inject and remove views from an xhtml page without a page refresh. I have no clue how to achieve this.

Even better would be a dynamic view injection based on urls. angularjs does that very well. But even without routing it would be great.

Thanks in advance.

Here is an example in pseudo code:

<nav>
    <h:link action="navigationBean.changeView(view1)" method="ajax">Link1</h:link>
    <h:link action="navigationBean.changeView(view2)" method="ajax">Link2</h:link>
</nav>

<h:viewContainer>
    // view selected by clicking the nav links should be injected here without page reload
</h:viewContainer>
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
arkhon
  • 765
  • 2
  • 11
  • 28
  • What do you mean with "view injection"? Please, provide an example for explanation. Also remember that JSF is a server side framework which just generates HTML code. You could combine it with Angular with no problem. – Aritz Jun 18 '14 at 14:55
  • changed my question to be more clear. – arkhon Jun 18 '14 at 16:09

2 Answers2

1

What you ask is better done using Facelet templating. You'll be able that way to have a page template with the shared content (the navigation menu in your case) and make the rest of the views inherit from it.

What can I see from your suggested solution is that you're abusing the POST calls. #{fragmentsPresenter.changeView('viewOne')} doesn't make sense just because you already know where you want to go to when you press that link (to viewOne), So you'll be better using plain links for that.

Here you've got an example showing how to handle navigation in a proper way. Let's suppose you've got a view controller even you won't need it in most of the cases:

ViewController.java

/**
 * Give the scope you want to your bean depending on what are your operations
 * oriented to. This example could be @ApplicationScoped as well
 * 
 * @author amaeztu
 * 
 */
@ManagedBean
@SessionScoped
public class ViewController {

    /**
     * Just gets the current view path and switches to the other one
     * 
     * @return
     */
    public String changeView() {
        String viewId = FacesContext.getCurrentInstance().getViewRoot()
                .getViewId();
        if (viewId.equals("/view1.xhtml")) {
            return "/view2";
        } else {
            return "/view1";
        }
    }
}

This controller's job is just to check what view are you coming from and switch to the other one. It's pointless to perform a POST request (to send a form) just to navigate to the other view, while you could evaluate it before page rendering.

Here you've got how the template view is built:

template.xhtml

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html">
    <h:head />

    <h:body>
        <h:form>
            <!-- Use POST requests like this only when you have 
            to determine the destination dinamically at server side -->
            <h:commandButton value="Switch View with POST"
                action="#{viewController.changeView}" />
            <br />
        </h:form>
        <!-- For plain navigation, use GET requests -->
        <h:link value="Go to view 1" outcome="/view1" />
        <br />

        <!-- Determine where we are at page rendering time 
            and evaluate the other view path -->
        <h:link value="Switch view without POST"
            outcome="#{view.viewId eq '/view1.xhtml' ? '/view2' : '/view1'}" />
        <br />
        <br />
        <ui:insert name="content" />
    </h:body>

</ui:composition>

This template page defines a shared button/link set and calls for content. I've implemented different kind of navigation options. Using <h:link /> is, in this case, the most straight-forward way. Check the second link, here we evaluate the current view id when it gets rendered and a link to go to the opposite one is created. Cool, isn't it?

Now here it is the implementation of the child views:

view1.xhtml

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html" template="/template.xhtml">

    <ui:define name="content">
        <!-- Here you could have some @ViewScoped 
        bean managing the content i.e. #{view1Bean} -->
        View 1
    </ui:define>

</ui:composition>

view2.xhtml

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html" template="/template.xhtml">

    <ui:define name="content">
        View 2
    </ui:define>

</ui:composition>

You'll be able to type their address in your browser and see them, that's what's called bookmarkable ;-)

See also:

Community
  • 1
  • 1
Aritz
  • 30,971
  • 16
  • 136
  • 217
  • Is it possible to only render with ajax? as it is it will refresh the whole page which i try to avoid. – arkhon Jun 19 '14 at 12:27
  • It is possible to design something similar as your posted solution, with an static page and central content rendered using Ajax. However, keep in mind that as I told before, GET requests are recommended for page to page navigation. Ajax postbacks are a handy tool to refresh some current view content or send partial form data. My tip is to avoid Ajax for any different usage. Other choice could be to use `ui:include` with no Ajax, but I think templates are the best solution for your case by far. Give them a try. – Aritz Jun 19 '14 at 13:28
0

Ok, i solved it as follows:

my View:

<h:body>

<nav>
<h:form>
    <h:commandLink action="#{fragmentsPresenter.changeView('viewOne')}">
        viewOne
        <f:ajax execute="@this" render="fragment-container" />
    </h:commandLink>
    <h:commandLink action="#{fragmentsPresenter.changeView('viewTwo')}">
        viewTwo
        <f:ajax execute="@this" render="fragment-container" />
    </h:commandLink>
</h:form>
</nav>

<h:panelGroup id="fragment-container">
    <ui:fragment rendered="#{fragmentsPresenter.activeView('viewOne')}">
        <div>i am view one!</div>
    </ui:fragment>
    <ui:fragment rendered="#{fragmentsPresenter.activeView('viewTwo')}">
        <div>i am view Two!</div>
        <ui:include src="fragment.xhtml"/>
    </ui:fragment>
</h:panelGroup>

and my ManagedBean:

@ManagedBean
@SessionScoped
public class FragmentsPresenter {
    private String activeView;

    public void setActiveView(String viewName) { this.activeView = viewName; }
    public String getActiveView() { return this.activeView; }

    public FragmentsPresenter() { this.activeView = "viewOne"; }

    public void changeView(String viewName) { this.activeView = viewName; }

    public boolean activeView(String viewName) {
        return activeView.equals(viewName);
    }
}
Aritz
  • 30,971
  • 16
  • 136
  • 217
arkhon
  • 765
  • 2
  • 11
  • 28
  • Well, it seems a proper solution. However, just a question, are that commandLinks a kind of menu for your whole application navigation? If yes, then you should go with templating, I consider it to be much more useful than rendering the same view once and again as you're doing. Mainly because it provides you with bookmarkable urls. – Aritz Jun 19 '14 at 06:40
  • I would be able to provide you a kickoff example if you want. – Aritz Jun 19 '14 at 09:20
  • sure, i would really appreciate that. – arkhon Jun 19 '14 at 09:33