Tuesday, July 14, 2009

Custom Components in JSF and af:iterator

I have something to report that I did not realize about custom UI components in JSF (lots of things probably...)

Consider the following scenario: you put a custom component in a af:iterator component. Can you answer the following questions?
  1. How many instances of your custom component that you included in that af:iterator loop will be created?
  2. If your component's function is to print a different kind of input field or widget depending on the parametric input given to the component tag, what might you need to do with any attributes in your component?

The answer to 1 is: one. One for each af:iterator. So if your iterator loop brings back 1000 records from the database, it loops 1000 times and "prints" your component 1000 times, but there is ever only one instance of the component.

For question 2...let us say that your component offers the printing of 4 different kinds of components depending on the value of its "type" tag attribute. So in that page you will have 1000 fields printed one for each af:iterator loop. If your component only rendered UIOutput components then it would suffice to have one property in your component class for each type of component.

But we are doing input components, so decode will be involved. That complicates things. JSF will generate a client id for each field that you generate with your component. If it is in the af:iterator loop it will look something like: formname:iteratorname:n:yourcomponentid -- where n is the zero-based loop index of your af:iterator.

On a JSF postback/decode, each field first runs the decode method. Then each field runs the encode methods for the component. In between the decode runs and the encode runs you need to store the submitted value of each component. I used a HashMap with client id as the key, and the component as the value. If you store everything away in decode you will have whatever you need to do the encode when it occurs for the postback.

Hope this helps someone.

2 comments:

Ramooza said...

Thanx a alot, this has been very helpfull specially the part of the number of instances that <af:iterator creates of the custom components.

But does this mean I can't create Nested Iterators and include custom components in?

for instance, consider this sample:

<af:iterator value="#{}">
<cus:custom1 />
<af:iterator value="#{}">
<cus:custom2 />
</af:iterator>
</af:iterator>

and inside custom2 implementation jspx there's some iterator as well

Michael A. Fons said...

Ramooza,

Thanks so much for your feedback.

Don't say "can't"; it's a mean, nasty, no-good, needlessly limiting word. :-)

I would recommend you do a small proof of concept, if you have not already.

There are a lot of variables here:
1. What is your custom component like?
2. What is your level of experience with JSF/custom components?
3. What do you want to achieve with these nested loops, combined with your custom component?
4. We could also ask questions 1. and 2. about ME as well, in terms of my ability to help you or not.

I have been doing some further reading in Schalk and Burns's JavaServer Faces: the Complete Reference, chapter 15, about the composite custom jsf component, RememberMeLoginComponent. What a great case study! I am learning so much...this may affect what I had to say about part 2. of this post; possibly even part 1. Hopefully if it does I will remember to come back and update this.

Michael F.