Hello Stephane,

Yes, I have dealt with the findComponent method and JSF's
name chaining scheme.  I agree with you that these should
not be hardcoded, which makes it hard to use the naming
convention unless you store some essential information in
the tag/component that helps find the other component.
Even then, you often have to hardcode the *structure* of
the path to the specific component.

I will share my experience and give an alternative to the
findComponent(String nameChain) method.

For accessibility purposes, our UI screens include an "access
navigation" menu that links to anchors within the same page.
Such as "<a href='#content'>Skip to Content Section</a>".  The
whole menu is kept in a "<ul><li><a ...></li>...</ul>" structure.
Also within the screen are the HTML anchor tags, such as
"<a name='content'></a>".

When converting to JSF components I decided to use two components:
UIScreenNavigation component (tag: <x:screenNav id='accessNav' />)
generates the link menu, but the component does not explicitly
know about the anchors (unlike with raw HTML coding).  The UIScreen-
Anchor component (tag: <x:screenAnchor name='content' for='accessNav'/>)
is responsible for telling the associated UIScreenNav component.
The 'for' attribute provides the ID of that component.

The <x:screenNav/> tag appears early in the screen, but <x:screenAnchor/>
tags can appear in any place and at any hierarchical level of the screen
design.  Therefore, in order to find the UIScreenNav component I could have
hardcoded that name chain into my code for the UIScreenAnchor component.

What I *really* wanted to do is find a component somewhere (anywhere) in
the component tree that was of a very specific type; in my case the UIScreen-
Navigation class.  So, using a trick from the JSP (not JSF) APIs and created
a method in a utility class that finds a component based on the component's
class.  I also wanted this pair of components to be generic such that two or
more anchor-based navigation menus could co-exist on the same screen.  I did
this (as you saw above) by using an 'id' attribute on the <x:screenNav/> tag
and a cooresponding 'for' attribute in the <x:screenAnchor/> tag.

Here is the code for these utility methods:

------------------------------------
    /**
     * This method finds a component within the root tree
     * that matches the ID and class of component.
     */
    public static UIComponent findComponentByClass(String id, Class compClass)
    {
        if ( id == null ) return null;
        if ( compClass == null ) return null;
        
        return findComponentByClass(getViewRoot(), id, compClass);
    }
    
    /**
     * This method finds a component within the root tree
     * that matches the ID and class of component.
     */
    public static UIComponent findComponentByClass(UIComponent withMe, String 
id, Class<?> compClass)
    {
        UIComponent result = null;
        
        if ( withMe == null ) return null;
        if ( id == null ) return null;
        if ( compClass == null ) return null;
        
        // Check if withMe *is* the component we are looking for
        if ( id.equals(withMe.getId()) )
        {
                if ( compClass.isAssignableFrom(withMe.getClass()) )
                {
                        return withMe;
                }
        }
        
        // Otherwise, check their childern or facets
        for ( Iterator it = withMe.getFacetsAndChildren(); it.hasNext(); )
        {
                UIComponent c = (UIComponent) it.next();
                result = findComponentByClass(c, id, compClass);
                if ( result != null ) break;
        }
        
        return result;
    }
------------------------------------

Once I had these methods, my coding task for UIScreenAnchor was
easy.  Here is the relevant part of this class:

------------------------------------
public class UIScreenAnchor extends UIComponentBase
{
        public static final String NAME_ATTR = "name";
        public static final String FOR_ATTR = "for";

        private String displayName = null;

        @Override
        public void setParent(UIComponent parent)
        {
                // Do the regular stuff
                super.setParent(parent);

                // Register this anchor with the screen navigation component
                String screenNavID = (String) getAttributes().get(FOR_ATTR);
                UIComponent c = 
ComponentUtils.findComponentByClass(screenNavID, UIScreenNavigation.class);
                if ( c != null )
                {
                        UIScreenNavigation screenNav = (UIScreenNavigation) c;
                        screenNav.addAnchor(this);
                }
        }
------------------------------------

I hope that I have given you some ideas to solve your
problem.

Regards,
Bryan




-----Original Message-----
From: [EMAIL PROTECTED] [mailto:[EMAIL PROTECTED]
Sent: Tue 5/22/2007 5:34 AM
To: [email protected]
Subject: Needs help for creating a new JSF component
 
Hi,

I have to develop a new JSF component (component, tag and renderer).

The goal of this new component is easy... Put together severals input zone in 
one single component.

I have done somethink that works... But i'm not sure I did everything 
correctly. (in the State of Art)
--> Should be great if I can get a link to a complete tutorial or full sample.

I create some code I don't like:
--> I have to "hardcoded" in the backingbean the "id" of my component and then 
cast it to my component.
In my backingbean, the code looks like this:
UIComponent temp = 
FacesContext.getCurrentInstance().getViewRoot().findComponent("dialog:dialog-body:mltext");
UIMltext myComponent = (UIMltext) temp;

During reading documentation I have found it is possible to "add" component in 
the JSF tree dynamicly with code like this:
public void addControls(ActionEvent actionEvent)
{
Application application = FacesContext.getCurrentInstance().getApplication();
//Question: how to get controlPanel ???
List children = controlPanel.getChildren();
children.clear();
for (int count = 0; count < numControls; count++)
{
HtmlOutputText output = 
(HtmlOutputText)application.createComponent(HtmlOutputText.COMPONENT_TYPE);
output.setValue(" " + count + " ");
output.setStyle("color: blue");
children.add(output);
}
}
Is it necessary in my component. What is the goal of adding component in the 
tree?

I can now do: "MyValue myValues = myComponent.getValue()"

Then I have to do somethink like:

backingBean.setValue1(myValues.getValue1());
backingBean.setValue2(myValues.getValue2());
...
backingBean.setValuex(myValues.getValuex());

Is it correct? I should prefer the setter are called by JSF layer. Is it 
possible?
Thanks a lot. 
Stéphane


<<winmail.dat>>

Reply via email to