Page Fragment Tips

7/16/07 Update: Thanks to Antonio, this blog entry is now available in Spanish.

Page fragments enable you to add a particular set of components, action handlers, and functions to a bunch of web pages. When you add a Page Fragment Box to a page, you are first given the option of choosing an existing page fragment or creating a new one, and then the IDE adds a <jsp:directive.include> tag to the main page (the page that includes the fragment). When you create a page fragment, the IDE adds both a .jspf file and a backing bean to the project.

The <jsp:directive.include> directive is not the same as, the <jsp:include> standard action. The directive causes the contents of the page fragment to be placed into the main page's .jsp before the .jsp file is translated into a servlet.

The page fragment's backing bean extends AbstractFragmentBean, which, unfortunately, does not provide the preprocess or prerender methods. It only provides the init and destroy methods. When a person lands on the page from another destination, such as by typing in the URL or clicking a link, the server instantiates the fragment's backing bean (and thus calls the init method) the first time that fragment is referenced. In general, this is likely to happen during the render response phase. On a postback (that is, when the page redisplays itself in response to a page submission) , the fact that the saved component state includes component bindings to properties in the fragment's backing bean will cause the server to instantiate the page fragment's backing bean much earlier.

Because the page fragment's init method can be called early in the lifecycle, you cannot reference or set component values in the init method. Conversely, a page fragment's init method might not be called until during or after the main page's prerender call. A workaround for this is to put methods in your page fragements and call those methods from the main page's prerender. For example, you can add a prerenderFragment method in each of your page fragments. Then for each fragment that you include in the main page, add code like the following:

public void prerender() {
      TopFragment topFragment = (TopFragment)getBean("TopFragment");
      topFragment.fragmentPrerender();
      ...
  }

Here are some code snippets to illustrate some common page fragment tasks:

Passing Data Between the Main Page and the Page Fragment

You can pass data to a page fragment through the a managed bean (request bean, session bean, application bean), or through function arguments as shown below

In the following snippit, the main page passes information to the page fragment by argument:

  public void prerender() {    
     Bike bike = getSessionBean1().getBike();
    ...
    PriceTable priceTable = (PriceTable) getBean("PriceTableFragment");
    totalsSubtotalPrice.setText(priceTable.subtotal(bike));
  }

Here, the main page sets the values in the request bean, which the page fragment then picks up and uses. This is from a web application where the user selects an item from a drop-down list in the main page, which, in turn, refreshes the trip data provider in the page fragment.

    // Page Fragment
    public void fragmentPrerender() {
        if (getRequestBean1().getPersonId() != null) {
            try {
                getSessionBean1().getTripRowSet().setObject(
                        1, getRequestBean1().getPersonId());
                tripDataProvider.refresh();
            } catch (Exception e) {
                error("Cannot switch to person " +
                        getRequestBean1().getPersonId().toString());
                log("Cannot switch to person " +
                        getRequestBean1().getPersonId().toString(), e);
            }
        }
    }
    // Main Page
    public void prerender() {
        // If not a postback, set the default person
        if (getRequestBean1().getPersonId() == null) {
            try {
                personDataProvider.cursorFirst();
                getRequestBean1().setPersonId(
                  (Integer)personDataProvider.getValue("PERSON.PERSONID"));
            } catch (Exception e) {
                error("Cannot switch to person " +
                        personDataProvider.getValue("PERSON.PERSONID"));
                log("Cannot switch to person " +
                        personDataProvider.getValue("PERSON.PERSONID"), e);
            }
        }
        Fragment1 fragment1 = (Fragment1) getBean("Fragment1");
        fragment1.fragmentPrerender();
    }	

Changing Component Properties in a Page Fragment

The main page can access and modify components in the page fragment through the page fragment's bean object, as shown below:

  public void prerender() { 
    Navigation navigationFragmentBean = (    
    Navigation)getBean("Navigation");
    Hyperlink homeLink = navigationFragmentBean.getHomeLink();
    homeLink.setDisabled(true);
 }

Determining What Page the Page Fragment is Included In

In this code snippet, the page fragment changes its apperance based on what page it is being displayed in.

    public void fragmentPrerender() {
        // If they are done, show the checkout button
        String page;
        String viewId = getFacesContext().getViewRoot().getViewId();
        if (viewId == null || viewId.length() == 0 || "/".equals(viewId)) {
            page = "";
        } else {
            int pos = viewId.lastIndexOf('/');
            if (pos == viewId.length() - 1) { 
                // last char in viewId is '/'
                viewId = viewId.substring(0, pos);
                pos = viewId.lastIndexOf('/');
            }
            //ok if pos is -1, then pos+1 will be 0
            page = viewId.substring(pos+1);
        }
        checkoutButton.setRendered(FINAL_ORDER_PAGE.equals(page));
    }

Using a Tab Set in a Page Fragment for Navigation

Yossarian, a Creator/NetBeans IDE user, submitted this tutorial to the Creator Community Docs Site: Simple Page Fragment Menu Example.

Dynamically Rendering a Page Fragment Component From the Main Page

Here is a snippit where the main page dynamically renders a tree in a page fragment

    public void makeTree() {
        Fragment2 pgfrag = (Fragment2)getBean("Fragment2");
        List children = pgfrag.getDisplayTree().getChildren();
        TreeNode personNode = new TreeNode();
        personNode.setId("node1");
        personNode.setText("Shari Oln");
        children.add(personNode);
    }

Other Examples

In Winston's blog he points to Josh's web site, which uses VWP page fragments to do wonderous things. How about you? Do you have some super page fragment tips to share? If so, post them to the comments below to share with other VWP developers.

Comments:

Sun recommends putting jspf page fragments into a WEB-INF/jspf folder so that the fragments are not directly accessible.
But you can't create a "Page Fragment" in WEB-INF with VWP (don't know about JSC) as you get an error "Name WEB-INF is an invalid folder name. Please specify a name that is a legal Java identifier".
I assume this error is because of the automatically generated backing bean rather than the JSP file itself as a plain jsp with no backing bean is fine under WEB-INF.
So you have to ignore Sun's recommendations and put JSF page fragments into a folder that is accessible - it's wise to protect this with a security constraint to stop anyone accessing it.

Posted by Aaron Davidson on June 24, 2007 at 10:18 PM PDT #

Hi Aaron,

Thanks for the great question. I spammed your question to the engineers and started up a good discussion. Sandeep says that you are correct that the IDE tries to put the backing bean in the WEB-INF package and package names cannot have dashes. Winston Prakash suggests that you use a Security Filter to disallow access to page fragments from the regular web page area. Several engineers noted that it is possible to not force the package structure to for the backing beans match the folder structure for the JSP files. Krys filed a bug (108079). You should go add your comments and vote for it. Thanks again.

Chris

Posted by diva #2 on June 25, 2007 at 11:32 AM PDT #

Is it possible for you to post the url to the recommendation that you quote?

Below are excerpts from the discussion that I had with the engineers in regards to putting the jsp in the WEB-INF folder:

In a pure JSP world with no frameworks, putting JSP sources in WEB-INF would be guaranteeing that your application would never ever work, because the container prevents direct access (by clients) to resources within the WEB-INF directory.

The only original justification for this that ever made sense was if you were using a framework like Struts where you didn't want the user accidentally navigating to the JSP page directly rather than going through the framework (typically a \*.do mapping for Struts). The advice that the user is relying on was written pre-JSF, and indeed likely would prevent any JSF based application (VWP or not) from working at all, either.

For JSF apps:

\* Leave the JSP pages (or the facelets pages, or whatever) in the main document root

\* If you are really itching to prevent direct access to the JSP pages (instead of going through /faces/\* or whatever), define a security constraint in web.xml that disallows access via that path.

The general idea would be to define a security constraint protecting "\*.jsp" URLs, but not including any role names in the <auth-constraint> element. Under the rather arcane semantics of security constraints, this means no direct access (by the client) to such a URL is allowed -- exactly the same "effect" people are after when putting things in WEB-INF (the container has an implicit constraint that disallows direct access to any /WEB-INF/\* URL).

...the whole "put jsp pages in WEB-INF" thing has always been a hack (security constraints are the right way to solve the perceived problem), and was used primarily to get around the way that RequestDispatcher works in the servlet API, which is still broken but it's too late to fix without substantial backwards incompatible changes -- ideally, Servlet 3.0 will define something that replaces it, however.

the URL showing in the location bar does not reflect the view (JSP or whatever) that actually rendered it. Instead, it reflects the last URL you had just posted to. This happens because the typical pattern in almost every Java based framework (including JSF, including Struts, ...) follows a pretty typical pattern:

\* POST of a form submit is mapped to some sort of processing logic (for a visual web app, the action method in your backing bean).

\* Processing logic does some stuff and returns a navigation indicator (for JSF, the outcome string)

\* Framework calls RequestDispatcher.forward() to the selected view resource -- sort of like a server-side redirect, but the key point is the browser does not know this happened, so the location URL still shows where the post went.

\* Selected view (typically a JSP page) creates the output.

Now, we've got a confusing URL showing, and if you bookmark it, the wrong thing happens. You can get around part of this in a JSF app by requiring a redirect in your navigation rule ... for other frameworks, it is not quite that easy.

RequestDispatcher was added in Servlet 2.0 (before there was a formal JCP process) as sort of a way to allow composability on the server side. Obviously, that means it predated a lot of the current art and science of bulding HTTP apps, or even the ideas of applying MVC principles to such apps. Unfortunately, that means there are ten years worth of Java software that depend on it, so you can't just throw it away ... but a green field development of servlets today would not do it that way.

Posted by diva#2 on June 28, 2007 at 01:11 AM PDT #

Thanks for the tips which are very helpful. I have a question to use the fragment component as a footer of a page. A header, left and footer is very common page design, but the footer doesn't perform well when there is a resizable component like table. I have been searching the forums and your colleague&rsquo;s blogs for better solutions. Unfortunately I haven't got one. I tried to use <f:verbatim> to wrap a html table and put the fragments part into the <td></td> pair. it can get the layout I expect, but the fragments fail to render any hyperlink etc correctly. I am wondering which the best way of achieving that is. Thanks.

Posted by Kane Li on July 02, 2007 at 07:28 PM PDT #

Diva#2 - This is one place where it is recommended to put jspf files under /WEB-INF/jspf :
http://java.sun.com/developer/technicalArticles/javaserverpages/code_convention/

Posted by Aaron Davidson on July 02, 2007 at 08:07 PM PDT #

In response to Kane Li.

Unfortunately, there is no easy way to have a floating page footer. It would have been nice if you could use a grid panel to layout the fragments in a page, but the IDE doesn't allow you put put page fragments inside grid panels.

Some people have found creative ways to get a floating footer. One person puts the footer html in a database table or in the application bean, and binds a text field to the data. Another person uses JavaScript to change the style property on load. (See http://forum.java.sun.com/thread.jspa?forumID=881&amp;amp;amp;threadID=5107064).

I have somewhat successfully created a page with a floating footer by setting the page's page layout property to flow layout. I then add a top page fragment, a grid panel, and then the bottom page fragment. I position all the page components in the group panel. Unfortunately, I have not figured out how to add a side panel using this method.

Also see Winston's tip at http://blogs.sun.com/winston/entry/css_layout.

Posted by Diva#2 on July 05, 2007 at 06:52 AM PDT #

Hi,
thanks for this tutorial.

I have a requirement to simulate html frames using page fragments.

For example, i have a list of hyperlinks on the left-pane of my page and i would like to show the Content on the right pane of my Page when the links are clicked.
So,the Question is how to display Content on a Page Fragemt when a hyperlink on the main page is clicked?

Regards.

Posted by Pasko on August 09, 2007 at 04:38 AM PDT #

With page fragments (which are actually include directives) you cannot swap out in the way that you ask (I don't think). The way I think it works is that the files are included at compile time (when the JSP is compiled into a servlet), and variables don't have values then. What you could do is put the list of links in a page fragment and include that as a sidebar in every page. So the contents becomes the main page and the page fragment is included in it. You can put code in the page fragment to determine what page is including the page fragment and you can turn off its link. Another alternative is to not use page fragments. Try this: Put two links on the side of the page and put a Group Panel on the right. Drop two Layout Panels in the Group Panel. Put a static text in each Layout Panel and set the texts to Panel1 and Panel2. Your prerender would look like: layoutPanel1.setRendered(true);layoutPanel2.setRendered(false);. hyperlink1_action would be: layoutPanel1.setRendered(true);
layoutPanel2.setRendered(false);
return null;. hyperlink2_action would be: layoutPanel2.setRendered(true);
layoutPanel1.setRendered(false);
return null;. Of course that could be a very complex page if you stuck your whole web site in it ;\^).

Posted by Diva#2 on August 09, 2007 at 12:50 PM PDT #

Hi Chris,

Thanks very much for your Response.
I have tried the first Option and it seems to work.
I will try the second Option too.

Regards,

Pasko

Posted by Pasko on August 13, 2007 at 10:56 PM PDT #

Nice Tutorial - however it tooks me ours to implement it - due to a smal detail:

When a Value, for example, the Text-Attribute of a staticText Field on the Pagefragment is set initially in the Desin-Editor to some Value, the work-aroung "fragmentPrerenderer(), etc." does not work !

This is not really a problem because one can leave the value blank in the Editor / JSP, however it can be time-consuming, missing this detail ...

Zeppo

Posted by Sebastian on September 03, 2007 at 11:11 PM PDT #

Zeppo, The problem that you encountered is not related to this page fragment tip or Visual Web apps but is related to general JSF implementation. This issue about JSF giving precedence to the values set in the JSP over the values set in the prerender when the user goes to the page from another (as opposed to a rerendering of the page) is explained in the Visual Web introductory tutorials such as http://www.netbeans.org/kb/55/vwp-helloweb.html, and http://www.netbeans.org/kb/55/vwp-about_components.html, as well as in the JSF specifications. When a page is first displayed in the browser, the page appears exactly as indicated by the tags in the JSP page. If your page bean has code that changes the property values, such changes appear only on requests where the page is submitted and subsequently redisplayed.

Posted by Diva #2 on September 07, 2007 at 07:25 AM PDT #

Hi

firstly thanks for this tutorial .
Secound i have the following question:
i am working with netbeans 6 , facelets and the standard woodstock components. My problem is that i couldn´t figure out how is it possible to show a FSF Page / or Pgae Fragment in a Facelet client template. The client template is shown in the browser (Firefox) but the included jspf Fragment not .

Please reply if you know any sollution for that .

Thanks in advance

Gergo

Posted by Gergely on January 30, 2008 at 09:48 PM PST #

Gergo,

I am sorry but I don't have an answer for you. I suggest that you try posting your question to nbusers at netbeans.org or users at visualweb.netbeans.org. See http://www.netbeans.org/community/index.html

Posted by Diva #2 on February 04, 2008 at 03:21 AM PST #

Post a Comment:
Comments are closed for this entry.
About

divas

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today