+1 on supporting an iterator around form items, I feel that would be
quite a powerful enhancement
On 6/5/07, *Adam Winer* < [EMAIL PROTECTED] <mailto:[EMAIL PROTECTED]>>
wrote:
On 6/4/07, * Matt Cooper* <[EMAIL PROTECTED]
<mailto:[EMAIL PROTECTED]>> wrote:
I think decoupling of these renderers would be a good thing.
The trick will be to make sure the form item renders properly
when it is a direct child of the form layout component. Let's
say that you put a tr:panelGroupLayout (or some other non form
layout component) inbetween the form layout component and the
form item--I feel that that particular form item should render
the same as if there was no form layout involved (this would
be insulating the form item from the form layout).
One of the big questions to decide when decoupling is whether you
want the parent component to augment/enhance the rendering of the
children or for the child to be passed more information to render
itself differently. In this case, I wonder if both might be
desirable, and I could imagine:
- A generic mechanism whereby a parent renderer could say "on my
children, here's a CoreRendererDecorator that I'd like to have
called for any child CoreRenderer", akin to a servlet fil
- A generic mechanism whereby a parent could publish rendering
information and child could ask for it, decoupled from looking at
the renderer and component (so we don't have getParent() or
getParent().getRenderer() and "instanceof" calls), something like
Object RenderingContext.getDescriptor(UIComponent parent)? This'd
let us have classes like FormLayoutData in the public API,
decoupling the renderers, without forcing us to move the renderers
themselves into the public API.
When considering a decoupling approach, you will also want
consider cases where a developer wants to have one kind of a
form layout component inside of another form layout
component. There will need to be some sort of mechanism to
ensure the correct rendering path is taken for the each
particular component hierarchy so a simple "am I contained, at
any level, in a form layout" might not be sufficient but an
"am I a direct child of a form layout" might.
Today, I believe if you have a tr:panelFormLayout inside of
another tr:panelFormLayout, the outer layout will have labels
on the side of the contents and the inner layout will have the
labels above the contents. This is determined by a request
attribute that is present only during the encodeAll of the
PanelFormLayout's encoding.
One final tip... There is one particular case where a form
item isn't a direct child of the form layout but will still
render as a form item: when it is contained in a tr:group
component and that tr:group is a direct child of the form
layout. The tr:group component is used by the form layout
renderer to draw separator lines between groups of form items.
And, in addition, I think a lot of people really wish that if you
had a tr:iterator inside of
a tr:panelFormLayout, that children of that iterator would render
as a form item. I think
a CoreRendererDecorator approach might accomplish that...
-- Adam
Regards,
Matt
On 5/24/07, *Leonardo Uribe* <[EMAIL PROTECTED]
<mailto:[EMAIL PROTECTED]>> wrote:
Hi everybody.
I'm working on a trinidad component for do layout (as part
of a Google Summer of Code) more easy and with less code
than panelFormLayout component.
Checking the code, I found a strong coupling between the
classes PanelFormLayoutRenderer and LabelAndMessageRenderer.
I found the following points:
1. LabelAndMessageRenderer has about 3 behaviors,
depending what is his parent components. In the code it
checks in a method something like
that:
private boolean _isFormRendererType(String rendererType)
{
return
"org.apache.myfaces.trinidad.Form".equals(rendererType) ||
"org.apache.myfaces.trinidad.FormLayout".equals(rendererType)
||
"org.apache.myfaces.trinidad.rich.Form
".equals(rendererType) ;
}
Because my component is a new component, i have to add a
line like this
return
"org.apache.myfaces.trinidad.Form".equals(rendererType) ||
"org.apache.myfaces.trinidad.FormLayout
".equals(rendererType) ||
"org.apache.myfaces.trinidad.rich.Form".equals(rendererType)
||
"org.apache.myfaces.
trinidad.TableLayout".equals(rendererType);
But this is very hacky. I have to do this because i need
that the method in encodeAll of LabelAndMessageRenderer
boolean needsPanelFormLayout = _isParentPanelForm(component);
returns true, because my component layout a table like
panelFormLayout.
2. In other part of the code, LabelAndMessageRenderer call
this method (in encodeAll)
if (needsPanelFormLayout)
{
if(PanelFormLayoutRenderer.encodeBetweenLabelAndFieldCells(context,
arc, rw))
{
renderRootDomElementStyles(context, arc,
component, bean);
}
}
What if my component has another behavior to this method?
PanelFormLayoutRenderer detects if this panelFormLayout is
inside
another panelFormLayout and because if it is the case, it
adds between Label and Field Cells something like this
rw.endElement("tr"); // label row
rw.startElement("tr", null); // field row
So, the label is rendered on top of the field.
3. LabelAndMessageRenderer do this for render a Label an a
field (please look the parts in yellow)
private void _renderLabelCell(
FacesContext context,
RenderingContext arc,
UIComponent component,
FacesBean bean,
boolean labelExists) throws IOException
{
ResponseWriter rw = context.getResponseWriter();
rw.startElement("td", null);
// render labelStyleClass and defaultStyleClass.
renderStyleClasses(context, arc, new String[]{
getLabelStyleClass(bean),
_getDefaultLabelStyleClass(arc,
SkinSelectors.AF_LABEL_TEXT_STYLE_CLASS )});
String labelInlineStyle = getLabelInlineStyleKey(bean);
rw.writeAttribute("style", labelInlineStyle, null);
String valign = getDefaultLabelValign(bean);
rw.writeAttribute ("valign", valign, null);
if (isDesktop(arc))
rw.writeAttribute("nowrap", Boolean.TRUE, null);
if (labelExists)
{
rw.writeAttribute("width",
arc.getProperties().get(_LABEL_CELL_WIDTH_KEY),
null);
}
delegateRenderer(context, arc, component, bean, _label);
rw.endElement ("td");
}
private void _renderFieldCell(
FacesContext context,
RenderingContext arc,
UIComponent component,
FacesBean bean,
boolean labelExists,
boolean needsPanelFormLayout,
boolean isInline) throws IOException
{
ResponseWriter rw = context.getResponseWriter();
rw.startElement("td", null);
rw.writeAttribute("valign", "top", null);
rw.writeAttribute("nowrap", Boolean.TRUE, null);
renderStyleClass(context, arc,
SkinSelectors.AF_CONTENT_CELL_STYLE_CLASS );
if (labelExists)
rw.writeAttribute("width",
arc.getProperties().get(_FIELD_CELL_WIDTH_KEY),
null);
renderFieldCellContents(context, arc, component, bean);
// The panelForm places messages below the fields, not
on a separate
// row:
if (needsPanelFormLayout)
{
// =-= mcc PPR PROBLEM!!! We should always be
rendering the "div",
// and always rendering an ID, if we ever want
it to be PPR
// replaceable:
if (isInline || hasMessage(context, arc, component,
bean))
{
rw.startElement("div", null);
renderStyleClass(context, arc,
SkinSelectors.AF_COMPONENT_MESSAGE_CELL_STYLE_CLASS);
_renderMessageCellContents(context, arc,
component, bean);
rw.endElement("div");
}
}
// bug 2484841: PDA: TOO MUCH WHITESPACE BETWEEN
// INPUT ELEMENTS IN LABELEDFIELD
// This is a browser bug workaround, hopefully we can
remove it eventually
if (isPDA(arc) && isIE(arc))
{
rw.startElement("div", null);
renderSpacer(context, arc, "1", "0");
rw.endElement("div");
}
rw.endElement("td");
}
I need to add an attribute in the td tag like <td
colspan=2 rowspan=3 height=XXX width=XXX .........>, but
if i want this, i need to
modify LabelAndMessageRenderer to recognize if the parent
component is my component, check if it has an attribute
like this
<mycomp:tableFormLayout
labelWidth="100" width="400" height="300"
fieldWidth="100" rows="100"
columns="1*;1*;1*" >
<tr:selectOneChoice
label="Salutation">
<f:selectItem itemLabel="1
Option" itemValue="1" />
<f:attribute name="spanXItem"
value="2"/>
<f:attribute name="spanYItem"
value="3"/>
</tr:selectOneChoice>
</mycomp:tableFormLayout>
and finally add a colspan or rowspan (height and width are
optional).
---------
Conclusion?: It's necesary to decouple
PanelFormLayoutRenderer and other FormRenderers with
LabelAndMessageRenderer in order to avoid
those hacks. I propose to create an interface that
implements some affected methods or create new ones, and
delegate this rendering to the parent
classes.
I want to you what should be better to do in that case. If
its necesary to refactor the classes, how it can be done.
Thanks for your attention
regards
Att: Leonardo Uribe
Ingeniero de Sistemas
Pontificia Universidad Javeriana
Ingeniero Electronico
Universidad Nacional de Colombia