You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@myfaces.apache.org by simon <si...@chello.at> on 2007/12/15 12:31:49 UTC

Re: Adding a property to a component and rendering it ... the magic way?

On Fri, 2007-12-14 at 20:07 -0600, Ole Ersoy wrote:
> Hi,
> 
> I have a attribute that I just need to get passed through to the corresponding html element.  For example:
> 
> <h:form id="registrationForm"
> 	anAdditionalAttribute="I need to get through to the form element">
> </h:form>
> 
> 
> In the rendered output I would like:
> 
> <form 
> 	...
> 	anAdditionalAttribute="I need to get through to the form element">
> </form>
> 
> 
> I think the process for doing this (Excluding component registration, etc.) is first to subclass the form component and add the "anAdditionalAttribute" to the subclass.  Then subclass the corresponding renderer and render this attribute.  
> 
> Does anyone know if there's a quicker/magic way to get the job done?

Every component has an "attributes" property that is a map that anything
can be stored in. It is common for properties that are not used by the
component itself (just the renderer) to be stored in this map, rather
than be on the component. The renderer can then retrieve them by:
  component.getAttributes().get("anAdditionalAttribute");

So that saves subclassing the component...

But the hard part is that to allow the tag to accept another attribute,
you need to subclass the tag class, and create a new taglib. Then your
page will need to use
  <foo:form ...>
rather than
  <h:form ...>

An alternative would be to write a custom tag that can be used to store
values into the attributes map of its enclosing component. Then
something like:
  <h:form ....>
    <foo:attribute name="anAdditionalAttribute" value="I need.."/>
could be used rather than creating a special tag for it.

As it happens, in MyFaces the standard f:attribute tag currently does
exactly this. Interestingly, it possibly shouldn't: the official JSF
docs say that it should only affect:
  "the closest parent UIComponent custom action"
which I guess means it should walk up the component tree to find a
subclass of UICommand. I'll investigate what the Sun RI implementation
does...

If using f:attribute is acceptable to you, that avoids creating a new
tag class too, leaving just the renderer code to be done.

Regards,

Simon


Re: Adding a property to a component and rendering it ... the magic way?

Posted by Ole Ersoy <ol...@gmail.com>.
Hey Guys,

I tried hacking the org.apache.myfaces.shared.renderkit.html.HTML constants contained in 

      <dependency>
        <groupId>org.apache.myfaces.shared</groupId>
        <artifactId>myfaces-shared-core</artifactId>
        <version>3.0.0</version>
        <scope>compile</scope>
      </dependency>

to see if I could add pass through attributes.  I did do a quick test to make sure the COMMON_PASSTHROUGH_ATTRIBUTES contained the new attributes, but it was still unsuccessful.

Which I think goes with 1.2.0?  My main reason for trying this is that it would save having to re-implement encodeBegin on the renderer which I think is necessary if I simply wanted to add another attribute and render it the normal way.

I'll probably just create a new component/renderer pair, since it seems to be the least amount of change to manage in the short run.

One thing that might save effort here is if the myfaces library had encodeBeginStartTag and encodeBeginEndTag template methods that could be overridden.  That way new attributes could be inserted without reimplementing all code in encodeBegin...Anyone like this idea?  Thus inside encodeBegin we would call these:

encodeBegin()
{
	encodeBeginStartTag(component, responseWriter)
	encodeBeginEndTag(component, responseWriter)
}

Then the subclassing renderer would just override encodeBeginStartTag and call it first.  Then renderer the additional attribute.  

The main rationale for doing stuff like this is that quickly adding attributes like "dojoType" would enable a client side javascript component to wrap the rendered element, enabling us to have both a server side and client side component tree.

Cheers,
- Ole


Re: Adding a property to a component and rendering it ... the magic way?

Posted by Ole Ersoy <ol...@gmail.com>.
Hi Simon and Andrew,

Thanks for the excellent advice.  The <f:attribute> type of tag sounds like the right way to go, especially coupled with modification suggested on the response writer.  I'll probably try to hack that in, assuming that <f:attribute> can be used with tags beside UICommand.  If it can't then I guess I'll have to create a new component that does the same, but with any subclass of UIComponent.   

In JSF in Action I think Kito recommended creating a HTMLRenderer parent class that has a utility method for rendering the UIComponent.attributes list / the pass through attributes.  Do you know if myfaces has something like this built in...that could be turned on via configuration.  This would save the renderer implementation as well.  To be safe I'm thinking it should be handled at the tag level.  So if the component tag had a renderPassThroughAttributes="true", then the renderer would render the pass through attributes.  Otherwise, we would get the current default behavior.  Thoughts?

Thanks again,
- Ole




simon wrote:
> On Fri, 2007-12-14 at 20:07 -0600, Ole Ersoy wrote:
>> Hi,
>>
>> I have a attribute that I just need to get passed through to the corresponding html element.  For example:
>>
>> <h:form id="registrationForm"
>> 	anAdditionalAttribute="I need to get through to the form element">
>> </h:form>
>>
>>
>> In the rendered output I would like:
>>
>> <form 
>> 	...
>> 	anAdditionalAttribute="I need to get through to the form element">
>> </form>
>>
>>
>> I think the process for doing this (Excluding component registration, etc.) is first to subclass the form component and add the "anAdditionalAttribute" to the subclass.  Then subclass the corresponding renderer and render this attribute.  
>>
>> Does anyone know if there's a quicker/magic way to get the job done?
> 
> Every component has an "attributes" property that is a map that anything
> can be stored in. It is common for properties that are not used by the
> component itself (just the renderer) to be stored in this map, rather
> than be on the component. The renderer can then retrieve them by:
>   component.getAttributes().get("anAdditionalAttribute");
> 
> So that saves subclassing the component...
> 
> But the hard part is that to allow the tag to accept another attribute,
> you need to subclass the tag class, and create a new taglib. Then your
> page will need to use
>   <foo:form ...>
> rather than
>   <h:form ...>
> 
> An alternative would be to write a custom tag that can be used to store
> values into the attributes map of its enclosing component. Then
> something like:
>   <h:form ....>
>     <foo:attribute name="anAdditionalAttribute" value="I need.."/>
> could be used rather than creating a special tag for it.
> 
> As it happens, in MyFaces the standard f:attribute tag currently does
> exactly this. Interestingly, it possibly shouldn't: the official JSF
> docs say that it should only affect:
>   "the closest parent UIComponent custom action"
> which I guess means it should walk up the component tree to find a
> subclass of UICommand. I'll investigate what the Sun RI implementation
> does...
> 
> If using f:attribute is acceptable to you, that avoids creating a new
> tag class too, leaving just the renderer code to be done.
> 
> Regards,
> 
> Simon
> 
>