Share via

Client Side validation on dynamically added Telephone objects to bootstrap modal form. Razor Pages

Pablo The Tiger 140 Reputation points
2026-05-03T01:32:40.07+00:00

Hello,

Lately I was receiving valuable help by @Jack Dang (WICLOUD CORPORATION) and @SurferOnWww . I'm working over their provided code mostly.

What I need now is to validate the new added Telephone objects (Description and PhoneNumber). The Contact fields are already validated correctly.

I attach the code about this in image (remember it doesn't authorize me to post code as text).

1 - The Bootstrap modal Form

forum 4

2 - The .cshtml Part 1

forum 1

2 - The .cshtml Part 2

forum 2

3 - The Telephone Model

forum 3

I will appreciate your help. Thank you in advance.

Pablo

Developer technologies | ASP.NET Core | Other

Answer accepted by question author

  1. Jack Dang (WICLOUD CORPORATION) 17,905 Reputation points Microsoft External Staff Moderator
    2026-05-04T04:11:38.27+00:00

    Hi @Pablo The Tiger ,

    Thanks for reaching out.

    In this case, reparsing the form alone is not enough. The newly created telephone inputs are being added with plain JavaScript, so Razor Tag Helpers do not get a chance to generate the validation attributes for them.

    For client-side validation to work on those dynamic fields, each generated input needs the same kind of data-val-* attributes that Razor would normally render, and each field also needs a matching validation message <span>. After adding the new HTML to the form, you can then reset and reparse unobtrusive validation.

    The snippets below are just for reference, so you may need to adjust the element IDs, property names, and layout classes to match the exact structure in your project.

    You can add a small helper like this:

    function refreshValidation() {
      const form = $('#myForm');
    
      form.removeData('validator');
      form.removeData('unobtrusiveValidation');
    
      $.validator.unobtrusive.parse(form);
    }
    

    Then, instead of creating the telephone inputs without validation metadata, create them with the required validation attributes included:

    function createTelephoneRow(key, phone) {
      const telephoneId = phone?.telephoneId ?? 0;
      const contactId = phone?.contactId ?? (document.getElementById('Id_Id')?.value || 0);
      const description = phone?.description ?? '';
      const number = phone?.number ?? '';
    
      const wrapper = document.createElement('div');
      wrapper.className = 'input-group mt-3 telephone-row';
    
      wrapper.innerHTML = `
        <input type="hidden" name="contact.Telephones.Index" value="${key}" />
        <input type="hidden" name="contact.Telephones[${key}].TelephoneId" value="${telephoneId}" />
        <input type="hidden" name="contact.Telephones[${key}].ContactId" value="${contactId}" />
    
        <div class="w-100 mb-2">
          <input type="text"
               class="form-control desc"
               name="contact.Telephones[${key}].Description"
               value="${description}"
               placeholder="Description"
               data-val="true"
               data-val-required="Description is required" />
    
          <span class="text-danger field-validation-valid"
              data-valmsg-for="contact.Telephones[${key}].Description"
              data-valmsg-replace="true"></span>
        </div>
    
        <div class="w-100 mb-2">
          <input type="text"
               class="form-control nmbr"
               name="contact.Telephones[${key}].Number"
               value="${number}"
               placeholder="Number"
               data-val="true"
               data-val-required="Phone number is required" />
    
          <span class="text-danger field-validation-valid"
              data-valmsg-for="contact.Telephones[${key}].Number"
              data-valmsg-replace="true"></span>
        </div>
    
        <button type="button"
            class="btn btn-danger"
            onclick="this.closest('.telephone-row').remove(); refreshValidation();">
          Remove
        </button>
      `;
    
      return wrapper;
    }
    

    Then your addTelephone() function can become:

    window.addTelephone = function () {
      const key = crypto.randomUUID();
      const wrapper = createTelephoneRow(key, null);
    
      document.getElementById('footer').before(wrapper);
    
      refreshValidation();
    };
    

    When you load the existing telephone records into the modal, use the same helper and call refreshValidation() after all rows are added:

    document.getElementById('telephonesContainer').innerHTML = '';
    
    for (let i = 0; i < data.telephones.length; i++) {
      const phone = data.telephones[i];
      const wrapper = createTelephoneRow(i, phone);
    
      document.getElementById('telephonesContainer').appendChild(wrapper);
    }
    
    refreshValidation();
    

    The important part is that this value:

    data-valmsg-for="contact.Telephones[0].Description"
    

    must match the input name exactly:

    name="contact.Telephones[0].Description"
    

    The same applies to Number. If those values do not match, validation may be registered, but the error message will not be displayed beside the correct field.

    Also keep the validation scripts on the page:

    @section Scripts {
      @{
        await Html.RenderPartialAsync("_ValidationScriptsPartial");
      }
    }
    

    Hope this helps! If my answer was helpful, I would greatly appreciate it if you could follow the instructions here so others with the same problem can benefit as well.

    Was this answer helpful?

    1 person found this answer helpful.

Answer accepted by question author

  1. SurferOnWww 6,016 Reputation points
    2026-05-04T00:34:32.9833333+00:00

    I suggest that you try not using a JavaScript to render the html code at the client side but having the ASP.NET render all the html code at the server side. It will enable the client side validation.

    To do so, consider using a partial view as I answered to your previous question How to read c# data object from server with AJAX and show its fields in html. When you use the partial view, all the attributes required for the client side validation are automatically rendered by the ASP.NET. Therefore, there is no need for the coding to include the attributes as mentioned in the other answers (IMO, it is not only cumbersome but also error-prone).

    Was this answer helpful?

    1 person found this answer helpful.

Answer accepted by question author

  1. Bruce (SqlWork.com) 84,061 Reputation points
    2026-05-03T23:39:04.4666667+00:00

    the unobtrusive validation library (https://github.com/aspnet/jquery-validation-unobtrusive) uses the jQuery validation library (https://github.com/jquery-validation/jquery-validation) under the covers. You should read the jquery validation docs. to understand how it's works.

    the unobtrusive library uses data-* attributes to collect the data to configure validation, and display errors. at page load ($.load()) it parses the data attributes. if you add new html dynamically, you must have unobtrusive, clear the old settings and read the new attributes. Your code is missing the validation data attributes. Its is also missing the span to display the validation error.

    sample code to add an input field for validation:

    <div>
        <form id="theForm">
            <div id="items">
            </div>
            <div>
                <button type="button" class="btn btn-primary" onclick="addItem()">Add Item</button>
                <button type="button" class="btn btn-primary" onclick="validateForm()">Validate</button>
            </div>
        </form>
    </div>
    
    @section Scripts {
        <partial name="_ValidationScriptsPartial" />
        <script>
            let itemIndex = 0;
            function addItem() {
                $('#items').append(`
                    <div class="form-group">
                        <label for="phone[${itemIndex}]">Phone:</label>
                        <input type="text" class="form-control" 
                            name="phone[${itemIndex}]" 
                            data-val="true"
                            data-val-required="The Phone field is required."
                            placeholder="Enter your phone number"><br />
                        <span class="text-danger field-validation-valid" 
                            data-valmsg-for="phone[${itemIndex}]" 
                            data-valmsg-replace="true"></span>
                    </div>`);
                itemIndex++;
                $("#theForm").removeData("validator").removeData("unobtrusiveValidation");
                $.validator.unobtrusive.parse('#theForm');
            }
            function validateForm() {
                $('#theForm').valid();
            }
        </script>
    }
    
    

    Was this answer helpful?

    1 person found this answer helpful.

1 additional answer

Sort by: Most helpful
  1. Q&A Assist
    2026-05-03T01:33:00.9266667+00:00
    AI generated content. This question contains an answer created with AI from Q&A Assist. Learn more

    Client-side validation for dynamically added Telephone fields requires two things:

    1. The correct validation scripts must be loaded.
    2. jQuery Unobtrusive Validation must be told to parse the dynamically added inputs after they are inserted into the DOM.

    From the code shown, the validation scripts are already included via:

    @section Scripts {
        @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
    }
    

    The missing step is reparsing the dynamic content. jQuery Unobtrusive Validation only parses the form once when the page loads. When new inputs are added in addTelephone or in openModalForm, call $.validator.unobtrusive.parse for the container that holds the new controls.

    Example using the existing telephonesContainer and addTelephone function:

    window.openModalForm = async function (id) {
        const url = '?handler=ContactData&Id=' + id;
        const response = await fetch(url);
    
        if (response.ok) {
            const data = await response.json();
            // ... existing code that fills contact fields and appends telephones ...
    
            // After all telephone inputs are appended
            $.validator.unobtrusive.parse('#myForm');
    
            const modalForm = new bootstrap.Modal(document.getElementById('modalForm'));
            modalForm.show();
        }
    }
    
    window.addTelephone = function () {
        const key = crypto.randomUUID();
        const wrapper = document.createElement('div');
        wrapper.className = 'input-group mt-3 telephone-row';
        wrapper.innerHTML = `
            <input type="hidden" name="contact.Telephones.Index" value="${key}" />
            <input type="hidden" name="contact.Telephones[${key}].TelephoneId" value="0" />
            <input type="hidden" name="contact.Telephones[${key}].ContactId" value="${document.getElementById('Id_Id').value || 0}" />
            <input type="text" class="form-control desc" name="contact.Telephones[${key}].Description" placeholder="Description" />
            <input type="text" class="form-control nmbr" name="contact.Telephones[${key}].Number" placeholder="Number" />
            <button type="button" class="btn btn-danger" onclick="this.parentElement.remove()">Remove</button>`;
    
        document.getElementById('footer').before(wrapper);
    
        // Reparse validation for the new controls
        $.validator.unobtrusive.parse('#myForm');
    };
    

    Key points:

    • The Telephone model already has [Required] attributes on Number and Description. Tag Helpers/HTML helpers will emit the corresponding data-val-* attributes when the inputs are rendered.
    • After dynamically adding the inputs, $.validator.unobtrusive.parse('#myForm') reparses the form, reads the data-val-* attributes, and wires up client-side validation for the new Telephone fields.
    • Client-side validation will then prevent form submission until all required Telephone Number and Description fields are filled.

    References:

    Was this answer helpful?

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.