You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by "Jochen Kemnade (JIRA)" <ji...@apache.org> on 2015/01/16 09:06:37 UTC
[jira] [Updated] (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:all-tabpanel ]
Jochen Kemnade updated TAP5-1512:
---------------------------------
Labels: bulk-close-candidate (was: ajax)
This issue has been last updated more than a year ago, has no assignee, affects an old version of Tapestry that is not actively developed anymore, and is therefore prone to be bulk-closed in the near future.
If the issue still persists with the most recent development preview of Tapestry (5.4-beta-26, available from Maven Central), please update it as soon as possible. In the case of a feature request, please discuss it with the Tapestry developer community on the dev@tapestry.apache.org mailing list first.
> 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
> Reporter: Adam Zimowski
> Priority: Minor
> Labels: bulk-close-candidate
>
> 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 was sent by Atlassian JIRA
(v6.3.4#6332)