0

I cannot display the validation error messages in dynamically loaded partial views.

enter image description here

I have a partial view _PrepareCCTable which contains dynamically loaded partial views of blank rows: _CCItemRow:

@model MyApp.Domain.Entities.CCItem

<div class="deduction-row form-group" id="divDeduction_@ViewBag.number">
<span class="deduction-number">@ViewBag.number</span>
<div>
    <input type="text" placeholder="Description" name="CCItems[@(ViewBag.number-1)].Description" id="CCItems[@(ViewBag.number-1)].Description" />
    @Html.ValidationMessageFor(model => model.Description, "", new { @class = "text-danger" })
</div>
<div>
    <input type="text" placeholder="Amount" name="CCItems[@(ViewBag.number-1)].Amount" id="CCItems[@(ViewBag.number-1)].Amount" class="deduction-amount" />
    @Html.ValidationMessageFor(model => model.Amount, "", new { @class = "text-danger" })
</div>
<a href="#" id="aRemoveDeduction_@ViewBag.number" class="remove-deduction"><span class="glyphicon glyphicon-remove-sign"></span></a>

and they are generated in the partial view using the following code:

 $("#aAddDeduction").click(function () {

            deductionCount += 1;

            $.ajax({
                url: "@Url.Action("CCItemRow", "CC")",
                cache: false,
                data: { number: deductionCount },
                success: function(html) { $("#divDeductions").append(html); }
            });

            return false;

and the model is simple:

public class CCItem
{
    [Key]
    public int CCItemID { get; set; }

    [Required]
    [DataType(DataType.Currency)]
    public decimal Amount { get; set; }

    [Required(ErrorMessage = "You must enter the description for the deduction item")]
    public string Description { get; set; }

}

and the respective controller code is here:

 public ActionResult PrepareCCTable()
    {
        return PartialView("_PrepareCCTable");
    }

    [HttpPost]
    public ActionResult PrepareCCTable(CCViewModel.PrepareCCModel viewModel)
    {
        if (ModelState.IsValid)
        {
            var file = repository.FindOAFile((int)Session["fileId"]);

            CCTable ccTable = new CCTable
            {
                CCItems = viewModel.CCItems,
                CreatedBy = HttpContext.User.Identity.GetUserId(),
                CreatedOn = DateTime.Now,
                FileID = file.FileID,
                MedEx = viewModel.MedEx,
                IsVATIncluded = viewModel.IsVATIncluded,
                GOP = viewModel.GOP,
                OAFile = file
            };

            repository.InsertCCTable(ccTable);
            repository.Save();

            return Json(new { saveSuccessful = true });
        }
        else
        {
            return PartialView("_PrepareCCTable", viewModel);
        }          
    }

    public PartialViewResult CCItemRow(int number)
    {
        ViewBag.number = number;
        return PartialView("_CCItemRow");
    }

The container partial view code:

@using MyApp.Domain.Entities
@model MyApp.Domain.Entities.CCViewModel.PrepareCCModel

@using (Ajax.BeginForm("PrepareCCTable", "CC", new AjaxOptions { HttpMethod = "POST", UpdateTargetId = "divContainer", OnSuccess = "handleSuccess" })) 
{

<div id="divContainer">

@Html.ValidationSummary(true, "Please check the values you entered for the following items and try again.")

<div class="form-group">
    <div class="col-md-3">MedEx</div>
    <div class="col-md-9">
        @*<input id="MedEx" name="MedEx" type="number" min="0" />*@
        @Html.EditorFor(model => model.MedEx, new { @class = "form-control"})
        @Html.ValidationMessageFor(model => model.MedEx, "", new { @class = "text-danger" })
    </div>
</div>

<div class="form-group">
    <div class="col-md-3">Is VAT Included?</div>
    <div class="col-md-9">
        <input type="radio" id="rbVATIncluded" name="IsVATIncluded" value="true" checked="checked" />VAT Included
        <input type="radio" id="rbVATNotIncluded" name="IsVATIncluded" value="false" />VAT NOT Included
    </div>
</div>

<div class="form-group">
    <div class="col-md-3">Amount to Perform Calculations on:</div>
    <div id="divAmount" class="col-md-9"></div>
</div>

<div class="form-group">
    <div class="col-md-3"></div>
    <div class="col-md-9">
        <div id="divDeductions">
            @*@if (Model != null)
            {
                if (Model.CCItems != null && Model.CCItems.Count > 0)
                {
                    for (int i = 0; i < Model.CCItems.Count; i++)
                    {
                        Html.RenderAction("CCItemRow", "CC", new { number = i + 1 });
                    }
                }
            }*@

        </div>
    </div>
</div>

<div id="divFunctions" class="form-group">
    <div class="col-md-3"></div>
    <div class="col-md-9">
        <div style="display: inline-block;"><a href="#" id="aAddDeduction">Add Deduction</a></div>
        <div style="display: inline-block;"><a href="#" id="aAddDiscount">Add Discount</a></div>
    </div>
</div>



<div id="divApplyDeductions" class="form-group">
    <div class="col-md-3"></div>
    <div class="col-md-9">
        <button id="btnApplyDeductions" type="button">Apply Deductions</button>
    </div>
</div>

<div id="divSubTotalAfterDeductions" class="form-group">
    <div class="col-md-3">Sub Total After Deductions:</div>
    <div id="subTotalAfterDeductions" class="col-md-9"></div>
</div>

<div id="divDiscount" class="form-group">
    <div class="col-md-3">Discount Rate: </div>
    <div class="col-md-9">
        <div>% <input id="discountRate" type="number" min="0" max="100" step="1" />&nbsp;<a href="#" id="aRemoveDiscount"><span class="glyphicon glyphicon-remove-sign"></span></a></div>
        <div style="display: inline-block;"><button id="btnApplyDiscount" type="button">Apply Discount</button></div>

    </div>
</div>

<div id="divSubTotalAfterDiscount" class="form-group">
    <div class="col-md-3">Sub Total After Discount: </div>
    <div class="col-md-9" id="subTotalAfterDiscount"></div>
</div>

<div id="divAddVAT" class="form-group">
    <div class="col-md-3">VAT: </div>
    <div class="col-md-9" id="VAT"></div>
</div>

<div id="divGOP" class="form-group">
    <div class="col-md-3">GOP: </div>
    <div class="col-md-9" id="GOPVal" name="GOPVal"></div>
    <input id="GOP" name="GOP" type="hidden" />
</div>

<div id="divSaveCC" class="form-group">
    <div class="col-md-3"></div>
    <div class="col-md-9"><input type="submit" class="btn btn-primary" value="Save" /></div>
</div>

</div>

}

<script type="text/javascript">

        function handleSuccess(result) {
            if (result.saveSuccessful) {

            } else {
                $('#divContainer').html(result);
            }
        }
 </script>
burakk
  • 1,231
  • 2
  • 22
  • 45
  • Your code to dynamically add new items is awful, and I suggest your use the `BeginCollectionItem` method - refer [this answer](http://stackoverflow.com/questions/40539321/a-partial-view-passing-a-collection-using-the-html-begincollectionitem-helper/40541892#40541892) for an example –  May 05 '17 at 10:19
  • I have already solved the issue. Thank you for your suggestion, but I would like to know why my code is awful? – burakk May 05 '17 at 14:01
  • 1
    Because (1) your using magic strings, and any change in your model would make it difficult to find the resulting error (2) your creating `ValidationMessageFor()` that have no relationship to your properties (no indexer, but I assume you worked that out (3) you cannot delete items without a lot of javascript to rename all indexers. Using the `BeginCollectionItem` method solves all this is far less code and your strongly binding to your model. –  May 07 '17 at 06:19
  • Thank you very much for your detailed explanation. I will definitely check BeginCollectionItem method out. – burakk May 07 '17 at 11:16

0 Answers0