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