Thursday, January 4, 2007

Dynamic JSF subviews

Dynamically switch between subviews in one main page

The easiest way would be the following:

<jsp:include page="#{myBean.includePage}" />

But this only works in JSF 1.2 + JSP 2.1 or newer. Unified Expression Language (the expressions starting with #) namely made its way from JSF to JSP in JSP 2.1. In older versioned environments the above snippet is not valid. To have JSP 2.1, you need a Servlet 2.5 compatible servletcontainer and a web.xml which is declared as per Servlet 2.5 specification.

The JSP 2.0 trick

If you're still using the old JSF 1.1 + JSP 2.0 (Servlet 2.4), then it's good to know that you can also access JSF managed beans using the old JSP EL notation ${managedBeanName}! You only have to keep in mind that this doesn't precreate the managed bean automatically when this line is called for the first time. So somewhere in the code before this line you should already have called the managed bean by JSF EL. The following example will work:

<h:panelGroup rendered="#{myBean.includePage != null}">
    <jsp:include page="${myBean.includePage}" />
</h:panelGroup>

Take care: do not use f:subview instead of h:panelGroup. Every include page should have its own f:subview with an unique ID.

The relevant java code of the backing bean MyBean.java should look like:

public String getIncludePage() {
    if (...) { // Do your thing, this is just a basic example with a nasty if-else tree.
        return "include1.jsp";
    } else if (...) {
        return "include2.jsp";
    } else if (...) {
        return "include3.jsp";
    } else {
        return "default.jsp";
    }
}

Copyright - There is no copyright on the code. You can copy, change and distribute it freely. Just mentioning this site should be fair.

(C) January 2007, BalusC

22 comments:

Bo said...

Hi!
Great job!!
I was thinking..Do you know a way of do this using NetBeans VWP??

Thanks in advance!

BalusC said...

I don't use the visual pack. Writing the code yourself will make a better developer of you.

Paul said...

When I try this I get an error saying:
The value of attribute "page" associated with an element type "jsp:include" must not include the '<' character.

I also had to put the scriptlet within jsp:scriptlet tags instead of the tags you had so it would be well formed xml.

Any ideas?

BalusC said...

That is indeed a problem if you declared your page as XML.

Anyway, this article is somewhat outdated. You can also use JSP EL and access a session attribute or even a request attribute. I.e.

jsp:include page="${sessionScope.currentInclude}"

Whereby a JSF backing bean can set it using FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put("currentInclude", "foo.jsp");

Paul said...

That worked!
thanks so much.
Paul

BalusC said...

The article is updated with somewhat better codings.

András said...

Hi BalusC!

Great job! Partially worked for me. Partially means in JSF 1.2, JSP 2.1, if I use like <jsp:include page="#{myBean.currentInclude}">, I get following error:

"The attributes for a standard action or an uninterpreted tag cannot be deferred expressions."

Any ideas what to do if you have @EJB annotations / EJBs injected by container? If you merely instantiate like new myPackage.MyBean(), EJBs are not injected, at least not on GlassFish v2 b58g, NetBeans 6.0.

BalusC said...

About that error message: make sure that you defined the web.xml to be at least version 2.5.

About EJB: I am not fully familiar with EJB 3.0 yet and so also not in a JSF context.

András said...

Thanx for the fast answer!

Unfortunately my web.xml looks like:

<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

JohnnyMa said...

Hi BalusC,

Thank for this great article! I tried to include my page with the following code,

< jsp:include page="#{myBean.currentInclude}" />

< t:outputText value="#{myBean.currentInclude}" />

but I only see my output string and not the include page. Any idea what I did wrong? thanks!

Johnny

Chuck said...

Outstanding articles. Thank you for sharing your knowledge.

I am new to the java enterprise development community and am curious why you use jsp as opposed to facelets?

Thanks again!

Krishna said...

how to pass parameter to the included jsp file?

BalusC said...

You can use the backing bean for it.

One Winged Angel said...

Thanks for your tutorial. Nowdays I suppose everybody using JSF 1.2 but I have to use 1.1 version due to project requeriments, so maybe you don't reply on this post anymore. I'm having problems using your solution because i'm using it inside an a4j:repeat. I want to create dynamically richfaces panelBarItems inside a panelBar. To do that i'm using the a4j:repeat. This is my code:


< rich:panelBar styleClass="empty">
  < a4j:repeat value="# studyManager.principalStudyTypes}" var="studyTypeBundle" >
    < rich:panelBarItem label="#{studyTypeBundle.studyType.name}">
      < h:panelGroup rendered="#{studyTypeBundle != null}">
        < jsp:include page="${studyTypeBundle.jspFile}"/>
      < /h:panelGroup>
    < /rich:panelBarItem>
  < /a4j:repeat>
< /rich:panelBar>

(spaces inside tags written deliberately to not be recognized as tags by blogger)

I get a null in "${studyTypeBundle.jspFile}". I've tried using "studyTypeBundle.jspFile != null" in the rendered atribute of the panel group too, but got the same results.

¿Any ideas? Thank you again.

neocygnus said...

work!!! but have a problem
components after of include append j_id_1 at end of id

this is very nasty but the validation over components dont redender the message because the for attribute dont equal with the generated id

Sorry for my English

JPP said...

Hi!, i could include a page, but the richtable into the page doesn't work.

Can I use this method to include a grid with catalog or orders?

I couldn't paste the code :(

JPP said...

My problem is the scroller.. the rest works fine.

Reshmi said...

Hi,

Is it possible to dynamically rerender a subview with another jsp page

after invoking an A4J request? What I need is something given below:

f:subview id="myView
jsp:include page="#{myBean.includeJsp}" /
/f:subview

a4j:commandButton id="doReRender" action="#{myBean.setNewPage}"

reRender=myview"/

MyBean.java:

public void setNewPage() {
this.includeJsp = "myNewPage.jsp";


FacesContext.getCurrentInstance().getExternalContext().getSessionMap()

.put("includeJsp", "myNewPage.jsp");
}

But doing this, is not having any effect upon completing A4j

action.But after next page refresh , it works fine. Could you please

suggest a solution for this?

Thanks in advance

tamizharasi said...

I am using JSF 2.0. So I need to write xhtml and I want to include jsp.

I am adding
html
xmlns="http://www.w3.org/1999/xhtml"
xmlns:jsp="http://java.sun.com/JSP/Page"
xmlns:c="http://java.sun.com/jsp/jstl/core"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html" as the header

and I gave jsp:include page="hai.jsp> But it is throwing http://java.sun.com/JSP/Page namespace not found. Could you please suggest something on this?

BalusC said...

You cannot mix JSP with Facelets. Facelets is the successor of JSP. Use Facelets <ui:include> instead.

Matt said...

Is there a way to dynamically add the jsp:include tag on a JSF page at runtime?

GDP said...

Hi! I am using h:datable inside the client that means inside ui:composition in JSF2. While navigating back from some other page, its saying duplicate id for the components reside in the datable. pls tell me how to resolve this.