Home > Back-end >  Append form field with model inside for loop (ASP.NET Razor Views)
Append form field with model inside for loop (ASP.NET Razor Views)

Time:12-26

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 >
                            @for (int i = 0; i < Model.NutritionFacts.Count(); i   )
                            {
                                <div >
                                    <div >
                                        <label>Nutrients name</label>
                                        <input type="text"  asp-for="@Model.NutritionFacts.ToList()[i].NutritionName">
                                        <span asp-validation-for="@Model.NutritionFacts.ToList()[i].NutritionName" ></span>
                                    </div>                                  
                                    <div >
                                        <label>Per 100g</label>
                                        <input type="text"  asp-for="@Model.NutritionFacts.ToList()[i].Gram">
                                        <span asp-validation-for="@Model.NutritionFacts.ToList()[i].Gram" ></span>
                                    </div>                                  
                                    <div >
                                        <label>% Daily value</label>
                                        <div >
                                            <input type="text"  asp-for="@Model.NutritionFacts.ToList()[i].PercentageDailyValue">
                                            <span asp-validation-for="@Model.NutritionFacts.ToList()[i].PercentageDailyValue" ></span>
                                            <a href="javascript:void(0)" >
                                                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 >
    <div >
        <label>Nutrients name</label>
        <input type="text"  asp-for="NutritionName">
        <span asp-validation-for="NutritionName" ></span>
    </div>                                  
    <div >
        <label>Per 100g</label>
        <input type="text"  asp-for="Gram">
        <span asp-validation-for="Gram" ></span>
    </div>                                  
    <div >
        <label>% Daily value</label>
        <div >
            <input type="text"  asp-for="PercentageDailyValue">
            <span asp-validation-for="PercentageDailyValue" ></span>
            <a href="javascript:void(0)" >
                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!

CodePudding user response:

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 >
        @for (int i = 0; i < Model.NutritionFacts.Count(); i  )
        {
            <div >
                <div >
                    <label>Nutrients name</label>
                    <input type="text"  asp-for="@Model.NutritionFacts.ToList()[i].NutritionName">
                    <span asp-validation-for="@Model.NutritionFacts.ToList()[i].NutritionName" ></span>
                </div>
                <div >
                    <label>Per 100g</label>
                    <input type="text"  asp-for="@Model.NutritionFacts.ToList()[i].Gram">
                    <span asp-validation-for="@Model.NutritionFacts.ToList()[i].Gram" ></span>
                </div>
                <div >
                    <label>% Daily value</label>
                    <div >
                        <input type="text"  asp-for="@Model.NutritionFacts.ToList()[i].PercentageDailyValue">
                        <span asp-validation-for="@Model.NutritionFacts.ToList()[i].PercentageDailyValue" ></span>
                        <a href="javascript:void(0)" >
                            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 >
    <div >
        <label>Nutrients name</label>
        <input type="text"  asp-for="NutritionName" name="[@i].NutritionName">
        <span asp-validation-for="NutritionName" ></span>
    </div>
    <div >
        <label>Per 100g</label>
        <input type="text"  asp-for="Gram"  name="[@i].Gram">
        <span asp-validation-for="Gram" ></span>
    </div>
    <div >
        <label>% Daily value</label>
        <div >
            <input type="text"  asp-for="PercentageDailyValue" name="[@i].PercentageDailyValue">
            <span asp-validation-for="PercentageDailyValue" ></span>
            <a href="javascript:void(0)" >
                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 void Index(List<ProductNutritionFactsDto> model)
{
    if(ModelState.IsValid)
    {
       //....
    }
    //.....
}

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.

CodePudding user response:

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" ></span>
  • Related