ajaxForm - dynamic required fields

I want to select the number of travelers on a registration form. According to the number, more fields appear for entering the names. These fields should then be required fields.

Call:

[[!AjaxForm?
    &snippet=`FormIt`
    &form=`tplFahrtenForm`
    &veranstalter=`[[+veranstalter]]`
    
	&preHooks=`` 
	&hooks=`formCustomFields,hook.RampartFormIt,spam,email,FormItAutoResponder`
	&emailSubject=`Anmeldung - [[*pagetitle]] - [[+veranstalter:select=`nuss=xxx&ars=xxxx`]]`
	&emailTpl=`fahrtenSend`
	&emailTo=`xxx@xxx.de`
	&emailCC=`xxx@xxx.de`
	&emailFrom=`xxx@xxx.de`
	&emailFromName=`[[+name]]`
	&emailReplyTo=`[[+email]]`
	&emailReplyToName=`[[+name]]`

	&rptErrorField=`rampart`
	
	&fiarTpl=`fahrtenSendFiar`
	&fiarSubject=`Sendebestätigung - Anmeldung [[*pagetitle]]`
	&fiarFrom=`xxx@xxx.de`
	&fiarFromName=`[[++site_name]]`	

	&validate=`
	name:required:stripTags,
		email:email:required,
		telefon:required:stripTags,
		datenschutz:required,
		teilnnr:minLength=7:maxLength=7:required,
		special:blank,
		text:stripTags
		`
	&successMessage=`[[$tplFahrtenThanks]]`
	&validationErrorMessage=`bitte füllen Sie alle Pflichtfelder korrekt aus. [[+errors]] `
	&datenschutz.vTextRequired=`Bitte akzeptieren Sie die Datenschutzerklärung`
        &emailMultiSeparator=`<br>`
    &name.vTextRequired=`Bitte gib den NAMEN an.`
    &telefon.vTextRequired=`Bitte gib die TELEFON NUMMER an.`
    &teilnnr.vTextRequired=`Bitte gib Deine TEILNEHMER-NUMMER an.`
    &teilnnr.vTextMinLength=`Die TEILNEHMER-NUMMER ist 7-stellig.`
    &teilnnr.vTextMaxLength=`Die TEILNEHMER-NUMMER ist 7-stellig.`
    &email.vTextRequired=`Bitte gib deine E-MAIL an.`
    &mitfahrer.vTextRequired=`Bitte gib alle MITFAHRER an.`
  
]]

tplFahrtenForm:

[[!+fi.validation_error_message:notempty=`<p class="error">[[!+fi.validation_error_message]]</p>`]]

[[+fi.AjaxForm.Message.error]]


<form action="[[~[[*id]]]]#formular" method="post" class="form ajax_form">
    <input type="hidden" name="nospam:blank" value="[[+fi.nospam]]" />
    <input type="hidden" name="reise" value="[[*pagetitle]]" />
    <input type="hidden" name="termin" value="[[*termin]]" />
    <input type="hidden" name="reiseveranstalter" value="[[+veranstalter:select=`nuss=xxx&ars=xxxx`]]" />
<div uk-grid class="uk-grid-small">
 
 <div class="uk-width-2-3@s">
    <input type="text" name="name" id="name" value="[[!+fi.name:htmlent]]" placeholder="Vorname, Name" >
	[[!+fi.error.name]]
  </div>
  
  <div class="uk-width-1-3@s">
    <input type="text" name="teilnnr" id="teilnnr" value="[[!+fi.teilnnr:htmlent]]" placeholder="Teilnehmer Nr." >
	[[!+fi.error.teilnnr]]
  </div>
 
   <div class="uk-width-1-2@s">
  
    <input type="email" name="email" id="email" value="[[!+fi.email:htmlent]]" placeholder="Email" >
	[[!+fi.error.email]]
  </div>
  <div class="uk-width-1-2@s">
    
    <input type="text" name="telefon" id="telefon" value="[[!+fi.telefon:htmlent]]" placeholder="Telefon" >
	[[!+fi.error.telefon]]
  </div>
  
  
  <div class="uk-width-1-2@s">
    <input type="number" min="0" name="dz" id="dz" value="[[!+fi.dz:htmlent]]" placeholder="Doppelzimmer Anzahl" > 
    [[!+fi.error.dz]]
  </div>
  
  <div class="uk-width-1-2@s">
    <input type="number" min="0" name="ez" id="ez" value="[[!+fi.ez:htmlent]]"  placeholder="Einzelzimmer Anzahl">
	[[!+fi.error.ez]]
  </div>
  
  <div class="uk-width-1-3@s">
      Anzahl aller Reisenden: 
   <select name="zahlPers" id="zahlPers">
     <option value="1" [[!+fi.zahlPers:is=`1`:then=`selected`]]>1</option>
     <option value="2" [[!+fi.zahlPers:is=`2`:then=`selected`]]>2</option>
     <option value="3" [[!+fi.zahlPers:is=`3`:then=`selected`]]>3</option>
     <option value="4" [[!+fi.zahlPers:is=`4`:then=`selected`]]>4</option>
     <option value="5" [[!+fi.zahlPers:is=`5`:then=`selected`]]>5</option>
     <option value="6" [[!+fi.zahlPers:is=`6`:then=`selected`]]>6</option>
     <option value="7" [[!+fi.zahlPers:is=`7`:then=`selected`]]>7</option>
     <option value="8" [[!+fi.zahlPers:is=`8`:then=`selected`]]>8</option>
     <option value="9" [[!+fi.zahlPers:is=`9`:then=`selected`]]>9</option>
     <option value="10" [[!+fi.zahlPers:is=`10`:then=`selected`]]>10</option>
</select>
  </div>
  
  <div class="uk-width-2-3@s">
      Mitfahrende Personen:
      
      <div id="mitfahrer"></div>
      
      
    <!-- <input type="text" name="mitfahrer1" id="mitfahrer" value="[[!+fi.mitfahrer1:htmlent]]" placeholder="Vorname, Name" >
	[[!+fi.error.mitfahrer1]]
  
    <input type="text" name="mitfahrer2" id="mitfahrer" value="[[!+fi.mitfahrer2:htmlent]]" placeholder="Vorname, Name" >
	[[!+fi.error.mitfahrer2]] -->
  </div>
  
  
  
  
  <div class="uk-width-1-1 fiSpecial">
   
    <input type="text" name="special" id="special" value="[[!+fi.special:htmlent]]" placeholder="Special" >
	[[!+fi.error.special]]
  </div>
  <div class="uk-width-1-1">
   
    <textarea name="text" id="text" value="[[!+fi.text]]" placeholder="Ihre Nachricht" >[[!+fi.text:htmlent]]</textarea>
	[[!+fi.error.text]]
  </div>
<div class="uk-width-1-1">

<input type="checkbox" name="datenschutz[]" id="datenschutz" value="Ja" [[!+fi.datenschutz:FormItIsChecked=`Ja`]] class="css-checkbox"><label for="datenschutz" class="css-label"><span></span>
    Ich stimme [[+veranstalter:is=`nuss`:then=`[[$agbNuss]]`:else=`[[$agbArs]]`]] zu.</label>[[!+fi.error.datenschutz]]
</div>

  <div class="uk-width-1-1">
    <input type="submit" value="Anmeldung senden" class="submit">
  </div>

<div class="uk-width-1-1 uk-text-right">
    *Pflichtfelder
    [[!+fi.error.rampart]]
  </div>
  </div>
    [[+fi.success:is=`1`:then=`
    <div class="alert alert-success">[[+fi.successMessage]]</div>
    `]]
    [[+fi.validation_error:is=`1`:then=`
    <div class="alert alert-danger">[[+fi.validation_error_message]]</div>
    `]]
</form>


<script>
 $(document).ready(function(){
     $("#zahlPers").on("change",function(){
     var numInputs = $(this).val();
     $('#mitfahrer').html('');
     for(var i=1; i < numInputs; i++)
     {
         var j = i*1;
         var $section =$('<input type="text" name="mitfahrer'+j+'" id="mitfahrer'+j+'" value="[[!+fi.mitfahrer'+j+':htmlent]]" placeholder="Vorname, Name" class="uk-margin-small-bottom" >[[!+fi.error.mitfahrer'+j+']]');

     $('#mitfahrer').append($section);
      }
   });
   
 });


</script>

Modx 2.8.3
PHP 7.4
AjaxForm 1.1.9
Formit 4.2.7

You probably have to create a custom validator for that.

&customValidators=`myValidator`
&validate=`zahlPers:myValidator, ...`

Then in the validator snippet myValidator, the variable $value contains the value of the field “zahlPers” and you should be able to read the values of the other fields with $validator->fields["mitfahrer1"] (or from $_POST) to check if they are filled in.

I created a customValidator. Unfortunately it does not work. What is wrong?

<?php
$value = $value;

$success = true;

$mitfahrer1 = $validator->fields["mitfahrer1"];
$mitfahrer2 = $validator->fields["mitfahrer2"];

if ($value == 2) {
    if (!$mitfahrer1) {
       $success = false;
        
    }
}

elseif ($value == 3) {
    if (!$mitfahrer1 || !$mitfahrer2) {
    $success = false;
    }
}

// ...

if(!$success){
	$validator->addError($key,'Bitte Mitfahrer angeben!');
}

return $success;

Not sure.
I ran a test with this form and your code as the “myValidator” validator and that seems to work.

[[!FormIt?
   &validate=`zahlPers:myValidator`
   &customValidators=`myValidator`
   &successMessage=`Success!`
]]

[[!+fi.successMessage]]
[[!+fi.validation_error_message]]

<form action="[[~[[*id]]]]" method="post">
    <span class="error_zahlPers">[[!+fi.error.zahlPers]]</span>
    <select name="zahlPers" id="zahlPers">
        <option value="1" [[!+fi.zahlPers:is=`1`:then=`selected`]]>1</option>
        <option value="2" [[!+fi.zahlPers:is=`2`:then=`selected`]]>2</option>
        <option value="3" [[!+fi.zahlPers:is=`3`:then=`selected`]]>3</option>
    </select>
    <input type="text" name="mitfahrer1" id="mitfahrer1" value="[[!+fi.mitfahrer1]]" />
    <input type="text" name="mitfahrer2" id="mitfahrer2" value="[[!+fi.mitfahrer2]]" />
    <input type="submit" value="Submit" />
</form>

Maybe make sure that the field exists before accessing it, to avoid errors:

$mitfahrer1 = isset($validator->fields["mitfahrer1"]) ? $validator->fields["mitfahrer1"] : "";

In principle, it seems to work. However, the phenomenon occurs when all “Mitfahrer” are filled in, the select field is empty. All option values have disappeared. So it does not longer checks the mandatory fields, if, for example, a passenger is deleted again.

Why do the options disappear? With the code that’s available to me, it is unclear why this could happen.


Maybe also add the “required” validator to the field “zahlPers” or check in your custom validator, if $value is a number between 1 and 10.

[[!FormIt?
   &validate=`zahlPers:required:myValidator`
   ...
]]