You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@myfaces.apache.org by Graeme Steyn <g....@cqu.edu.au> on 2007/07/20 12:56:16 UTC

Validating Multiple Components in DataTable

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/%3c55
97324.post@talk.nabble.com%3e.  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);
        
    }


RE: Validating Multiple Components in DataTable

Posted by Graeme Steyn <g....@cqu.edu.au>.
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:delbd+jakarta@oma.be]
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/%3c5597324.post@talk.nabble.com%3e.
> 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/




Re: Validating Multiple Components in DataTable

Posted by David Delbecq <de...@oma.be>.
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/%3c5597324.post@talk.nabble.com%3e. 
> 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/