David,
 
Thank you for the suggestion.  I have taken a look at the "validateEqual" 
example and produced a "AllOrNone" item that works in a similar way.  The main 
differences being that it is attached to any field that is not part of the 
validation, appearing after the fields being checked in the row and the 
 
uiComponent.getParent().findComponent(id)
 
code has been changed to 
 
UIComponent parent = uiComponent.getParent();
            while(!(parent instanceof NamingContainer)) {
                parent = parent.getParent();
            }
 
parent.findComponent(id);
 
This is due to the fact that the tr:inputText is inside a tr:column which is 
inside tr:table.  The easiest thing to do was to navigate to the 
NamingContainer, before using findComponent().  Final page looks something like 
this:
 
<tr:table
        id="tblx"
        value="#{bean.collection}"
        binding="#{bean.coreTable}"
        var="entry" >

        <tr:column>
            <tr:inputText value="#{entry.prop1}  id="prop1"/>
        </tr:column>

        <tr:column>
            <tr:inputText value="#{entry.prop2}" id="prop2" />
        </tr:column>
...
        <tr:column>
            <tr:selectBooleanCheckbox id="propx" ...>
                 <my:allOrNoneValidator componentIds="prop1,prop2,prop3" />
            </tr...>
 
This results in a message being displayed in the column for propx for every row 
that fails the validation.
 
I did implement a NotNullIfOtherNotNull, but found that if the field it is 
attached to has a null value, then the validation never gets triggered.  
 
As additional feedback, I have also implemented the validator in the backing 
bean to provide the message at the table level should any row fail validation.  
I will get feedback on which version the users want and retain it.  Code 
appears below.
 
    /**
     * Iterate through component tree and confirm that input values for selected
     * items are not null.
     */
    private void getStatusBits(UIComponent uiComponentIn) {
        
        Iterator <UIComponent> iterator = 
uiComponentIn.getChildren().iterator();
        while (iterator.hasNext()) {
            UIComponent uiComponent = iterator.next();
            String family = uiComponent.getFamily();
            if (!(uiComponent instanceof CoreInputHidden) 
                    && (CoreInputText.COMPONENT_FAMILY.equals(family) ||
                    CoreSelectOneChoice.COMPONENT_FAMILY.equals(family))) {
                statusBits <<= 1;
                if (((EditableValueHolder) uiComponent).getLocalValue() != null
                        && ((EditableValueHolder) uiComponent).isValid()
                        && !((EditableValueHolder) 
uiComponent).getLocalValue().toString().isEmpty()) {
                    statusBits ^= 1;
                }
                return;
            }
            getStatusBits(uiComponent);
        }
        
        return;
    }
    
    /**
     * Validate contents for the table component provided using the expected
     * bitmap indicating which fields are to be provided.
     */
    private void validateTableComponent(CoreTable table,
            final int requiredFieldMap) throws ValidatorException  {
        
        UIXCollection uixCollection = (UIXCollection) table;
        int oldRowIndex = uixCollection.getRowIndex();
        for (int rowNum = 0, numRows = uixCollection.getRowCount(); rowNum < 
numRows; rowNum++) {
            uixCollection.setRowIndex(rowNum);
            statusBits = 0;
            getStatusBits(uixCollection);
            
            if ((statusBits > 0) && ((statusBits ^ requiredFieldMap) > 0)) {
                
                FacesMessage message = Messages.getMessage(
                        Constants.LOCALIZATION_RESOURCE,
                        "eduIncompleteRecord", null);
                message.setSeverity(FacesMessage.SEVERITY_ERROR);
                uixCollection.setRowIndex(oldRowIndex);
                throw new ValidatorException(message);
                
            }
        }
        uixCollection.setRowIndex(oldRowIndex);
        
    }
    
    /**
     * Verify that entries contain all required data, if present.
     */
    public void validateEntriesComplete(FacesContext facesContext,
            UIComponent uIComponent,
            Object object) throws ValidatorException {
        
        if (object == null) {
            return;
        }
        
        validateTableComponent(table, 0x0000000F); //Alter 0X0000000F based on 
the number of fields that
                                                                               
//must have values.
        
    }

    private int _statusBits;
 
    <tr:message
          id="msg"
          for="validate" />
                
                <tr:table...
 
                </tr:table>
 
    <tr:inputHidden
           id="validate"
           value="valid?"
           validator="#{bean.validateEntriesComplete}" />
 
A bit messy, but does the trick for my scenario and the coding should improve 
as I learn more :-)
 
Thanks again.
 
Graeme.
 
________________________________

From: David Delbecq [mailto:[EMAIL PROTECTED]
Sent: Fri 20/07/2007 10:18 PM
To: MyFaces Discussion
Subject: Re: Validating Multiple Components in DataTable



Quick suggestion:
<tr:table
        id="tblx"
        value="#{bean.collection}"
        binding="#{bean.coreTable}"
        var="entry" >

        <tr:column>
            <tr:inputText value="#{entry.prop1}  id="prop1">
                <my:NotNullIfOtherNotNull others="prop2,prop3,prop4"/>
            </tr:inputText>
        </tr:column>

        <tr:column>
            <tr:inputText value="#{entry.prop2}" id="prop2" >
....

as for the content of the cutsom my:NotNullIfOtherNotNull, i suggest you
look at code of "validateEqual" in tomahawk, that check 2 component
value are same. You can do similar thing with null.
That way, non need to limit your check to the use of a UIData.


En l'instant précis du 20/07/07 12:56, Graeme Steyn s'exprimait en ces
termes:
> Hi,
> 
> I have a <tr:table> component that is displaying rows from an
> ArrayLIst containing instances of class X.  I would like to introduce
> a validator that checks that if information is entered for any
> property, then all properties must be populated for that row.  ANy
> rows that have no data entered or all data entered are thus valid. At
> present I have the arrangement provided below, but I appear to be
> getting the instances of X from the original collection, rather than
> the table components local values.  Thus when the view is first
> displayed, I can enter data in row 1 cell 1 and press next to invoke a
> POST back and the validation.  Using the debugger, I find that the
> first iteration through the loop returns an instance of X, but without
> the posted back data (everything is null).  I do not appear to be
> getting at the local converted value.
> 
> Any help would be greatly appreciated.
> 
> Thank you,
> 
> Regards,
> 
> Graeme.
> 
> PS.
> I came across a related query located at
> http://mail-archives.apache.org/mod_mbox/myfaces-users/200608.mbox/[EMAIL 
> PROTECTED]
> I am trying to avoid binding as the user will be able to add and
> delete rows within my datatable, so I was hoping to keep things flexible.
> 
> public class X {
> 
> private String prop1;
> private String prop2;
> private String prop3;
> 
> getters/setters...etc
> }
> 
> ====================
> 
> class BackingBean {
> 
> Collection <X> collection = new ArrayList <X> ();
> 
> etc
> }
> 
> ====================
> 
> <tr:message for="validateRows" />
> 
> <tr:table
>         id="tblx"
>         value="#{bean.collection}"
>         binding="#{bean.coreTable}"
>         var="entry" >
> 
>         <tr:column>
>             <tr:inputText value="#{entry.prop1} />
>         </tr:column>
> 
>         <tr:column>
>             <tr:inputText value="#{entry.prop2} />
>         </tr:column>
> :
> :
> etc
> </tr:table>
> 
> <tr:inputHidden id="validateRows" value="dummy"
> validator="#{bean.validateRows}" />
>
> =====================
> 
> Will only catch the first invlaid instance in the table - the
> validation message appears at the table level rather than per row.
> 
>     public void validateEntriesComplete(FacesContext facesContext,
>             UIComponent uIComponent,
>             Object object) throws ValidatorException {
>        
>         UIXCollection uixCollection = (UIXCollection) coreTable;
>         int oldRowIndex = uixCollection.getRowIndex();
>         for (int rowNum = 0, numRows = uixCollection.getRowCount();
> rowNum < numRows; rowNum++) {  
>             uixCollection.setRowIndex(rowNum);
>             X instancex = (X) uixCollection.getRowData();
>           
>             if (!instancex.isAllPropertiesSet()
>             && instancex.isAnyPropertySet()) {
>               
>                 FacesMessage message = Messages.getMessage(
>                         Constants.LOCALIZATION_RESOURCE,
>                         "IncompleteRecord", null);
>                 message.setSeverity(FacesMessage.SEVERITY_ERROR);
>                 throw new ValidatorException(message);
>               
>             }
>         }
>         uixCollection.setRowIndex(oldRowIndex);
>       
>     }


--
http://www.noooxml.org/



<<winmail.dat>>

Reply via email to