You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by "Adam Zimowski (JIRA)" <ji...@apache.org> on 2011/04/26 17:01:04 UTC

[jira] [Commented] (TAP5-1512) Select update via zone inside a form confuses form's ValidationTracker

    [ https://issues.apache.org/jira/browse/TAP5-1512?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=13025263#comment-13025263 ] 

Adam Zimowski commented on TAP5-1512:
-------------------------------------

Complete Thread: http://tapestry.1045711.n5.nabble.com/T5-select-zone-update-in-a-form-tp4314488p4314488.html

> Select update via zone inside a form confuses form's ValidationTracker
> ----------------------------------------------------------------------
>
>                 Key: TAP5-1512
>                 URL: https://issues.apache.org/jira/browse/TAP5-1512
>             Project: Tapestry 5
>          Issue Type: Bug
>          Components: tapestry-core, tapestry-ioc
>    Affects Versions: 5.2.5
>            Reporter: Adam Zimowski
>            Priority: Minor
>
> Here is the use case:
> We have a simple address form with Line 1, Line 2, City (all text fields), State (Select), Zip (Text Field) and Country (Select). State select is wrapped inside a Zone, which is tied to Country select, so that when country is selected, states get populated. Line 1, City, State, Zip and Country are all required. Form uses <t:error/> next to each field.
> Here is the problem:
> 1. Select country and state (both have blank options therefore are required), but leave other fields empty.
> 2. Submit form. Validation on required fields (such as city, zip) results in form rendering error messages. Note: Country and State are
> not in error at this point.
> 3. While correcting errors on the form, change country. As a result, state select component is repopulated (zone update), and default blank
> option is select. Do not chose state.
> 4. Submit form with state *not* selected. Debugging select shows that ValidationTracker contains error (state required), yet attached field error is not displaying the error.
> Upon debugging the framework I found the following:
> IdAllocator used to generate component id, suffixes select with an increment even select is not inside a loop. It does this ONLY when Zone is updated, causing the very subsequent form submit record error for select under wrong key (a_state_1 rather than state), and so the <t:error/> is unable to locate the error when form eventually renders back with original select id (a_state).
> In this case, what happens is that every time I update zone while form is in error, the ID of my select changes with incremented suffix:
> ORIGINAL AS EXPECTED BY FORM: a_state
> After resetting country causing state dropdown to repopulate, id becomes: 
> a_state_1 then a_state_2 etc...
> This causes ValidationTracker put error under wrong key, and consequently error to field binding in tracker's map cannot be
> resolved, causing <t:error/> thinking that all is good. That's why <t:errors/> does display the error, as it simply loops over collection
> of errors.
> Out of lack of deep understanding of Tapestry, I coded a simple hack to verify that if select update via zone kept its original id things
> would work, and indeed, the following hack fixes the problem for my case:
> Tapestry 5.2.5, AbstractField line 183:
>    private void setupControlName(String controlName)
>    {
>        if(controlName.startsWith("a_state"))
>                this.controlName = "a_state";
>        else
>                this.controlName = controlName;
>    }
> Here is my source:
> <t:form t:id="registrationForm">
> <div class="kk-hdr">Address Information</div>
>  <div class="kk-row">
>  <div class="kk-label"><t:label for="a_line1"/> :</div>
>  <div class="kk-field"><t:textfield t:id="a_line1" value="address.line1"/></div>
>  <t:error class="literal:kk-error" for="a_line1"/>
>  </div>
>  <div class="kk-row">
>  <div class="kk-label"><t:label for="a_line2"/> :</div>
>  <div class="kk-field"><t:textfield t:id="a_line2" value="address.line2"/></div>
>  <t:error class="literal:kk-error" for="a_line2"/>
>  </div>
>  <div class="kk-row">
>  <div class="kk-label"><t:label for="a_line3"/> :</div>
>  <div class="kk-field"><t:textfield t:id="a_line3" value="address.line3"/></div>
>  <t:error class="literal:kk-error" for="a_line3"/>
>  </div>
>  <div class="kk-row">
>  <div class="kk-label"><t:label for="a_city"/> :</div>
>  <div class="kk-field"><t:textfield t:id="a_city" value="address.city"/></div>
>  <t:error class="literal:kk-error" for="a_city"/>
>  </div>
>  <div class="kk-row">
>  <div class="kk-label"><t:label for="a_zip"/> :</div>
>  <div class="kk-field"><t:textfield t:id="a_zip" value="address.zipCode"/></div>
>  <t:error class="literal:kk-error" for="a_zip"/>
>  </div>
>  <div class="kk-row">
>  <div class="kk-label"><t:label for="a_state"/> :</div>
>  <div class="kk-field" t:type="zone" t:id="stateModelZone"><t:select
> t:id="a_state" model="stateModel" validate="required"
> value="address.stateCode" blankOption="ALWAYS"
> blankLabel="literal:--Please Select"/></div>
>  <t:error class="literal:kk-error" for="a_state"/>
>  </div>
>  <div class="kk-row">
>  <div class="kk-label"><t:label for="a_country"/> :</div>
>  <div class="kk-field"><t:select t:id="a_country" model="countryModel"
> value="address.countryCode" blankOption="NEVER"
> zone="stateModelZone"/></div>
>  </div>
>  <p>
>  <input t:type="submit" value="message:submit-label"/>
>  </p>
> </t:form>
> public class Register extends BasePage {
>        @Inject
>        private Logger log;
>        @Inject
>        private UtilityServiceRemote utilityService;
>        @Persist
>        @Property
>        private AddressUiBean address;
>        @OnEvent(value=EventConstants.PREPARE)
>        void initialize() {
>                if(address == null) address = new AddressUiBean();
>                if(contact == null) contact = new ContactUiBean();
>                if(registration == null) registration = new RegisterUiBean();
>                String countryCode = address.getCountryCode();
>                if(countryCode == null) {
>                        Locale locale = getLocale();
>                        countryCode = locale.getCountry();
>                        address.setCountryCode(countryCode);
>                }
>                log.debug("address state code {}", address.getStateCode());
>        }
>        @Cached
>        public Map<String, String> getCountryModel() {
>                Map<String, String> model = new LinkedHashMap<String, String>();
>                List<CountryBean> countries =
>                        utilityService.getAllCountries(getLocale());
>                for(CountryBean country : countries) {
>                        String code = country.getCodeIsoAlpha2();
>                        String description = country.getShortName();
>                        log.debug("code: {}, description: {}", code, description);
>                        model.put(code, description);
>                }
>                return model;
>        }
>        @OnEvent(value=EventConstants.VALUE_CHANGED, component="a_country")
>        public Object onCountrySelected(String aCountryCode) {
>                log.debug("selected country: {}", aCountryCode);
>                address.setStateCode(null);
>                return stateModelZone.getBody();
>        }
>        @Cached
>        public Map<String, String> getStateModel() {
>                Map<String, String> model = new LinkedHashMap<String, String>();
>                String countryCode = address.getCountryCode();
>                List<StateProvinceBean> states =
>                        utilityService.getAllStateProvincesForCountry(countryCode, getLocale());
>                for(StateProvinceBean state : states) {
>                        String code = state.getLookupCode();
>                        String name = state.getLongName();
>                        log.debug("code: {}, name {}", code, name);
>                        model.put(code, name);
>                }
>                return model;
>        }
> }

--
This message is automatically generated by JIRA.
For more information on JIRA, see: http://www.atlassian.com/software/jira