1

This is my first question here at stackoverflow. Hehehe.

I have an edit page which I need to display all the existing fields and I have an add button which I can add a new field.

Here is the source code for edit in which where the displaying of data happens (Edit.cshtml)

                                <div class="nutrition-field">
                            @for (int i = 0; i < Model.NutritionFacts.Count(); i++ )
                            {
                                <div class="nutrition-div form-group row">
                                    <div class="col-lg-4">
                                        <label>Nutrients name</label>
                                        <input type="text" class="nutrients-name form-control" asp-for="@Model.NutritionFacts.ToList()[i].NutritionName">
                                        <span asp-validation-for="@Model.NutritionFacts.ToList()[i].NutritionName" class="text-danger"></span>
                                    </div>                                  
                                    <div class="col-lg-4">
                                        <label>Per 100g</label>
                                        <input type="text" class="gram form-control" asp-for="@Model.NutritionFacts.ToList()[i].Gram">
                                        <span asp-validation-for="@Model.NutritionFacts.ToList()[i].Gram" class="text-danger"></span>
                                    </div>                                  
                                    <div class="col-lg-4">
                                        <label>% Daily value</label>
                                        <div class="d-flex">
                                            <input type="text" class="percentage-value form-control mr-5 w-50" asp-for="@Model.NutritionFacts.ToList()[i].PercentageDailyValue">
                                            <span asp-validation-for="@Model.NutritionFacts.ToList()[i].PercentageDailyValue" class="text-danger"></span>
                                            <a href="javascript:void(0)" class="delete-btn btn btn-fixed-height btn-secondary font-weight-bold font-size-sm px-8">
                                                Delete
                                            </a>
                                        </div>
                                    </div>
                                </div>
                            }   
                            </div>

And here is the javascript for the add button:

            $(addNutrition).click(function() {
                $.ajax({
                url: "/Product/AddNutrition",
                cache: false,
                success: function (html) {
                    $('.nutrition-field').append(html)
                }
            });
        });

When you click the button it will go to the controller and here is the code for the Add Nutrition Controller:

       public IActionResult AddNutrition()
    {
        return PartialView("~/Views/Product/PartialViews/_AddNutrition.cshtml", new ProductNutritionFactsDto());
    }

Here is the Partial View:

@model ProjectName.Dto.ProductNutritionFactsDto

<div class="nutrition-div form-group row">
    <div class="col-lg-4">
        <label>Nutrients name</label>
        <input type="text" class="nutrients-name form-control" asp-for="NutritionName">
        <span asp-validation-for="NutritionName" class="text-danger"></span>
    </div>                                  
    <div class="col-lg-4">
        <label>Per 100g</label>
        <input type="text" class="gram form-control" asp-for="Gram">
        <span asp-validation-for="Gram" class="text-danger"></span>
    </div>                                  
    <div class="col-lg-4">
        <label>% Daily value</label>
        <div class="d-flex">
            <input type="text" class="percentage-value form-control mr-5 w-50" asp-for="PercentageDailyValue">
            <span asp-validation-for="PercentageDailyValue" class="text-danger"></span>
            <a href="javascript:void(0)" class="delete-btn btn btn-fixed-height btn-secondary font-weight-bold font-size-sm px-8">
                Delete
            </a>
        </div>
    </div>
</div>

It's working but I wanted it to be inside the for loop so the counting of index will continue and it will have a unique ID. What is happening is it is creating a new model and the validations are not working when adding many fields.

I would really appreciate your help everyone. Thank you!

mitokki
  • 13
  • 2

2 Answers2

2

I saw you use @Model.NutritionFacts.ToList()[i].PropertyName in main view and ProductNutritionFactsDto in partial view. Assume that your main view accept a model contains nested List<ChildModel> called ProductNutritionFactsDto.

For your scenario, you need pass the count to ajax and use ViewBag to store the count and use it in the partial view:

Main View:

model TestViewModel
<button id="addNutrition" type="button">Add</button>
<form method="post">
    <div class="nutrition-field">
        @for (int i = 0; i < Model.NutritionFacts.Count(); i++)
        {
            <div class="nutrition-div form-group row">
                <div class="col-lg-4">
                    <label>Nutrients name</label>
                    <input type="text" class="nutrients-name form-control" asp-for="@Model.NutritionFacts.ToList()[i].NutritionName">
                    <span asp-validation-for="@Model.NutritionFacts.ToList()[i].NutritionName" class="text-danger"></span>
                </div>
                <div class="col-lg-4">
                    <label>Per 100g</label>
                    <input type="text" class="gram form-control" asp-for="@Model.NutritionFacts.ToList()[i].Gram">
                    <span asp-validation-for="@Model.NutritionFacts.ToList()[i].Gram" class="text-danger"></span>
                </div>
                <div class="col-lg-4">
                    <label>% Daily value</label>
                    <div class="d-flex">
                        <input type="text" class="percentage-value form-control mr-5 w-50" asp-for="@Model.NutritionFacts.ToList()[i].PercentageDailyValue">
                        <span asp-validation-for="@Model.NutritionFacts.ToList()[i].PercentageDailyValue" class="text-danger"></span>
                        <a href="javascript:void(0)" class="delete-btn btn btn-fixed-height btn-secondary font-weight-bold font-size-sm px-8">
                            Delete
                        </a>
                    </div>
                </div>
            </div>
        }
    </div>
    <input type="submit" value="post" />
</form>

@section Scripts
{
<script>
    $("#addNutrition").click(function () {
        $.ajax({
            url: "/Product/AddNutrition?index=" + $(".nutrients-name").length,
            cache: false,
            success: function (html) {
                $('.nutrition-field').append(html)
            }
        });
    });
</script>
}

Controller:

public IActionResult AddNutrition(int index)
{
    ViewBag.Index = index;
    return PartialView("~/Views/Product/PartialViews/_AddNutrition.cshtml", new ProductNutritionFactsDto());
}

Partial View:

Get the ViewBag.Index and add name attribute with this index.

@model ProductNutritionFactsDto
@{ 
    var i = (int)ViewBag.Index;
}
<div class="nutrition-div form-group row">
    <div class="col-lg-4">
        <label>Nutrients name</label>
        <input type="text" class="nutrients-name form-control" asp-for="NutritionName" name="[@i].NutritionName">
        <span asp-validation-for="NutritionName" class="text-danger"></span>
    </div>
    <div class="col-lg-4">
        <label>Per 100g</label>
        <input type="text" class="gram form-control" asp-for="Gram"  name="[@i].Gram">
        <span asp-validation-for="Gram" class="text-danger"></span>
    </div>
    <div class="col-lg-4">
        <label>% Daily value</label>
        <div class="d-flex">
            <input type="text" class="percentage-value form-control mr-5 w-50" asp-for="PercentageDailyValue" name="[@i].PercentageDailyValue">
            <span asp-validation-for="PercentageDailyValue" class="text-danger"></span>
            <a href="javascript:void(0)" class="delete-btn btn btn-fixed-height btn-secondary font-weight-bold font-size-sm px-8">
                Delete
            </a>
        </div>
    </div>
</div>

Testing model:

public class TestViewModel
{
    public List<ProductNutritionFactsDto> NutritionFacts { get; set; }
}
public class ProductNutritionFactsDto
{
    [Required]
    public string NutritionName { get; set; }
    public string Gram { get; set; }
    public string PercentageDailyValue { get; set; }
}

Besides, notes for further coding(about posting the data to backend):

asp-for="@Model.NutritionFacts.ToList()[i].PropertyName" will generate the name attribute: name="[i].PropertyName", so if you want to post all the ProductNutritionFactsDto models in the page, be sure the backend parameter should be List<ProductNutritionFactsDto>.

[HttpPost]
public IActionResult Index(List<ProductNutritionFactsDto> model)
{
    if(ModelState.IsValid)
    {
       //....
    }
    return View(new TestViewModel() { NutritionFacts=model});
}

If you just want to pass any one ProductNutritionFactsDto model in the page separately, be sure add name attribute(name="PropertyName") to override the asp-for generated name and the backend parameter should be ProductNutritionFactsDto.

Rena
  • 30,832
  • 6
  • 37
  • 72
0

enter image description here

The first form group is the one in for loop, before adding a new field so the validation is working when clicking the submit button. The second form group is the one after clicking the add button.

How can I use the asp-validation-for? I tried this one but it's not working.

    <span asp-validation-for="[@i].NutritionName" class="text-danger"></span>
mitokki
  • 13
  • 2
  • I have tried but still not working... :( – mitokki Dec 26 '21 at 14:44
  • Hi, asp-validation-for will not work for your scenario, suggest using server side validation `ModelState.IsValid` to judge the validation. It works fine. – Rena Dec 27 '21 at 01:52
  • May I ask why it will not work? But yes, I am using also the `ModelState.IsValid` I am just trying to check if asp-validation-for will work also. Thank you so much!!! – mitokki Dec 28 '21 at 04:55
  • I agree your idea use something like `asp-validation-for="[@i].NutritionName"`. Your code will generate `data-valmsg-for="i"` in html instead of `data-valmsg-for="[3].NutritionName"`. Also I try to make generated html correctly, but the validation also does not work, it seems the ajax append the html cannot be validated. – Rena Dec 28 '21 at 06:30
  • Ohh, I see. Thank you so much!!! Do you have any idea or approach for this kind of behavior so that the asp-validation-for will work? – mitokki Dec 28 '21 at 18:14
  • Hi, you may need custom client side validation. You can refer to this requiredIfBusinessTypeValidate.js in [this answer](https://stackoverflow.com/a/65148764/11398810). – Rena Dec 29 '21 at 01:15