You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@myfaces.apache.org by St...@ext.ec.europa.eu on 2007/05/22 13:34:41 UTC

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


RE: Needs help for creating a new JSF component

Posted by Bryan Basham <bb...@stillsecure.com>.
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: Stephane.CLINCKART@ext.ec.europa.eu [mailto:Stephane.CLINCKART@ext.ec.europa.eu]
Sent: Tue 5/22/2007 5:34 AM
To: users@myfaces.apache.org
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



Re: Needs help for creating a new JSF component

Posted by David Delbecq <de...@oma.be>.
There are tutorial on how to create components and renderes.
Now while your goal is easy, it's realization isn't, creating composite
components is not straightfoward in JSF.
I see 4 ways to do so

1) don't use child components, just render several form fields in the
renderer and decode them yourself
2) During your component initialisation create child components
(getChildren().add(....)). The tricky part is to not redo it when it's a
restore view (because JSF recreated child components for you). The
easiest way is to do it lazily and store in your main component state a
'childCreated' property
3) Use facelets, which can easily be used to create composite components
4) Create the whole composite component tree in a bean and use binding.

Storing your component clientId inside a bean should not be necessary.
You always have your component as parameter when you need it in JSF
(render, restore, apply values, ...).

En l'instant précis du 22/05/07 13:34,
Stephane.CLINCKART@ext.ec.europa.eu s'exprimait en ces termes:
>
> 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
>


Re: Needs help for creating a new JSF component

Posted by David Delbecq <de...@oma.be>.
There are tutorial on how to create components and renderes.
Now while your goal is easy, it's realization isn't, creating composite
components is not straightfoward in JSF.
I see 4 ways to do so

1) don't use child components, just render several form fields in the
renderer and decode them yourself
2) During your component initialisation create child components
(getChildren().add(....)). The tricky part is to not redo it when it's a
restore view (because JSF recreated child components for you). The
easiest way is to do it lazily and store in your main component state a
'childCreated' property
3) Use facelets, which can easily be used to create composite components
4) Create the whole composite component tree in a bean and use binding.

Storing your component clientId inside a bean should not be necessary.
You always have your component as parameter when you need it in JSF
(render, restore, apply values, ...).

En l'instant précis du 22/05/07 13:34,
Stephane.CLINCKART@ext.ec.europa.eu s'exprimait en ces termes:
>
> 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
>