After more debugging, the problem seems to be with Tapestry's IdAllocator and how it generates client side id's for select components, particularly when Zone is updated.
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; } I tried filing this in JIRA but it seems to be down (Bad Gateway). In any case, I do not know enough about framework internals to fix this properly. Additional insight would be highly appreciated. Adam On Thu, Apr 21, 2011 at 11:33 AM, Adam Zimowski <zimowsk...@gmail.com> wrote: > Okay, now I am pretty sure this is a bug related to usage of > <t:error/> in 5.2.5. If I put <t:errors/> (which I didn't have > before), the state required error shows up as expected. If I use > <t:error/> attached to state field, the error does not show. > > It's like looking for a needle in a haystack.. lol..... still could > appreciate suggestion if anyone knows how to temporarily fix this. > > Adam > > On Thu, Apr 21, 2011 at 11:14 AM, Adam Zimowski <zimowsk...@gmail.com> wrote: >> Quick update: >> >> By debugging Select component I see that validation tracker correctly >> records the error when I submit the blank option for state. The blank >> option is submitted throug the following use case: >> >> 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 it >> records error. Yet form is not displaying the error. >> >> So the problem is that on the first submit Tapestry is not rendering >> the error, which is really there. Bug? >> >> Adam >> >> >> >> On Thu, Apr 21, 2011 at 10:20 AM, Adam Zimowski <zimowsk...@gmail.com> wrote: >>> I'm sorry, I am on Tapestry 5.2.5 :-) >>> >>> On Thu, Apr 21, 2011 at 10:19 AM, Adam Zimowski <zimowsk...@gmail.com> >>> wrote: >>>> @Josh - When I debug Select in 5.2.4 (break on processSubmission(), >>>> line 166), selectedValue is blank which is expected. So select >>>> correctly submits without a value, it's just it seems that the >>>> validator does not recognize that select state was repopulated via >>>> zone and consequently no value was submitted the 2nd time. It seems to >>>> me like the form validator somehow things that select has the value >>>> from the prior submission, but it really doesn't since it was reset >>>> via zone update. >>>> >>>> @Mark - by validation kicks in, I meant that form was submitted and >>>> validated. I have a debug on state code passed from the state select >>>> component when form activates, and AddressUiBean comes back with the >>>> correct value. That is, when I select the state from the dropdown, >>>> AddressUiBean carries the state as expected. Even when form is in >>>> error, as I change the country thereby causing state dropdown get >>>> repopulated, and resubmit the form with blank option for state (did >>>> not select the state), AddressUiBean shows null state code (as >>>> expected). Yet the form does not report the error on the required >>>> state field. >>>> >>>> Adam >>>> >>>> On Wed, Apr 20, 2011 at 10:25 PM, Mark <mark-li...@xeric.net> wrote: >>>>> When you say "Validation kicks in" does this occur after a submit? If so, >>>>> is it possible that AddressUIBean is remembering the value that was >>>>> present >>>>> when the submit occurred and then on the second submit it is getting set >>>>> to >>>>> null again? >>>>> >>>>> At the point when you would expect an error to occur, what is the value of >>>>> the state field? That should give you a good clue as to what is >>>>> happening. >>>>> >>>>> Mark >>>>> >>>>> On Tue, Apr 19, 2011 at 5:11 PM, Adam Zimowski <zimowsk...@gmail.com> >>>>> wrote: >>>>> >>>>>> I have a typical address form with street, city zip textfields and two >>>>>> dropdowns: country and state. The state dropdown is wrapped in a zone >>>>>> so that when country is selected, states are populated: >>>>>> >>>>>> <div class="kk-field" t:type="zone" t:id="stateModelZone"><t:select >>>>>> t:id="a_state" model="stateModel" validate="required" >>>>>> value="stateKode" blankOption="ALWAYS" blankLabel="literal:--Please >>>>>> Select"/></div> >>>>>> >>>>>> All works okay, except for a test case my business analyst found which >>>>>> I can't figure out. >>>>>> >>>>>> You fill out a form, pick a country then pick a state and suppose you >>>>>> leave city field empty which is required. Validation kicks in. While >>>>>> correcting a city error, you decide to switch a country causing state >>>>>> list to get repopulated and state be not selected again. Suppose you >>>>>> submit the form, I expect the error that state is required, but error >>>>>> is not thrown. Only if I resubmit the form a second time, state will >>>>>> be flagged in error. >>>>>> >>>>>> I am resetting state to null on country change. I have even tried >>>>>> setting form state field in error, none of which works. >>>>>> >>>>>> ---------- TML -------------- >>>>>> >>>>>> <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> >>>>>> >>>>>> ---------------- Page class ------------------------- >>>>>> >>>>>> 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; >>>>>> } >>>>>> } >>>>>> >>>>>> Adam >>>>>> >>>>>> --------------------------------------------------------------------- >>>>>> To unsubscribe, e-mail: users-unsubscr...@tapestry.apache.org >>>>>> For additional commands, e-mail: users-h...@tapestry.apache.org >>>>>> >>>>>> >>>>> >>>> >>> >> > --------------------------------------------------------------------- To unsubscribe, e-mail: users-unsubscr...@tapestry.apache.org For additional commands, e-mail: users-h...@tapestry.apache.org