1

On the same view I am returning a list. Also, I have [HttpPost] to create new entry. When the model is not valid, The ValidationSummary shows that expenseName is Required.

I would like to show Model required error Messages near each field in my View. asp-Validation-for was working when I didn't have IEnumerable model. What I have to do so I can see the validation error messages using IEnumerable model? (etc. for expenseName)

Model

[Key]
public int expenseId { get; set; }
[Column(TypeName = "nvarchar(50)")]
[Required(ErrorMessage = "The field 'Expense Name' is required")]
public string expenseName { get; set;}
public double price { get; set; }
public bool isStandard { get; set; }
public DateTime? date { get; set; }

View

@model IEnumerable<ExpenseTracker.Models.Expenses>
@Html.ValidationSummary(false, "", new { @class = "text-danger" })
@{
    ViewData["Title"] = "Index";
}


<form method="post" enctype="multipart/form-data" asp-controller="Expenses" asp-action="Index">
    <div>
        <label for="files" class="lbl-select-file">Select Expense File</label>
        <input id="files" name="postedFiles" class="btn-uploader" type="file">
    </div>
    <label class="expense-name-lbl" for="expenseName">Expense Name</label>
    <input type="text" name="expenseName" class="expense-name-input" id="expenseName" />
    **<span asp-validation-for="  " class="text-danger"></span>**
    <div class="form-group form-check">
        <label class="form-check-label checkbox-isStandard-form">
            <input class="form-check-input" type="checkbox" name="chkeco" title="Check this if the bill is monthly/yearly" /> Is Standard?
        </label>
    </div>
    <input type="submit" class="btn btn-success btnColor-expenses" value="Upload" />
    <label id="fileName"></label>
    <span class="uploaded-file">@Html.Raw(ViewBag.Message)</span>
</form>
<table class="table table-text">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.expenseName)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.price)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.isStandard)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.date)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model)
        {
            <tr>
                <td>
                    @Html.DisplayFor(modelItem => item.expenseName)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.price)
                </td>
                <td>
                    <input type="checkbox" class="checkbox-isStandard" checked="@item.isStandard" />
                </td>
                <td>
                    @if (item.date.HasValue)
                    {
                        DateTime? datetimeToDate = @item.date;
                        string FormattedDate = datetimeToDate.Value.ToString("dd-MM-yyyy");
                        <label>@FormattedDate</label>

                    }
                </td>
                <td>
                    <a asp-action="Edit" asp-route-id="@item.expenseId">Edit</a> |
                    <a asp-action="Details" asp-route-id="@item.expenseId">Details</a> |
                    <a asp-action="Delete" asp-route-id="@item.expenseId">Delete</a>
                </td>
            </tr>
        }
    </tbody>
</table>

Controller

 [HttpPost]
        [ValidateAntiForgeryToken]
        [PreventDuplicateRequest]
        public async Task<IActionResult> Index(IFormCollection formCollection, string expenseName, 
            List<IFormFile> postedFiles, [Bind("expenseName,price,isStandard,date")] Expenses expenses)
        {
            if (ModelState.IsValid)
            {
              ......
                return View(await _context.Expenses.ToListAsync());
            }
            else
            {
                return View(await _context.Expenses.ToListAsync());
            }
jps
  • 20,041
  • 15
  • 75
  • 79
andreaspan
  • 11
  • 1

1 Answers1

0

You can create a ViewModel which contains a List<Expenses>(you wanna display in the view) and a Expenses(you wanna upload to the controller).

ViewModel

public class ExpensesViewModel
    {         
        //Pass the value to this parameter in the get method to display on the page
        public List<Expenses> ListModel { get; set; }
        public Expenses model { get; set; }
    }

Then change your view code like:

@model ExpensesViewModel

    @Html.ValidationSummary(false, "", new { @class = "text-danger" })
@{
    ViewData["Title"] = "Index";
}


<form method="post" enctype="multipart/form-data" asp-controller="Home" asp-action="Show">
    <div>
        <label for="files" class="lbl-select-file">Select Expense File</label>
        <input id="files" name="postedFiles" class="btn-uploader" type="file">
    </div>
    <div class="form-group">
        <label class="expense-name-lbl" for="expenseName">Expense Name</label>
        <input type="text" \ class="expense-name-input" asp-for=@Model.model.expenseName />
        <span asp-validation-for="@Model.model.expenseName" class="text-danger"></span>
    </div>
    <div class="form-group form-check">
        <label class="form-check-label checkbox-isStandard-form">
            <input class="form-check-input" type="checkbox" name="chkeco" title="Check this if the bill is monthly/yearly" /> Is Standard?
        </label>
    </div>
    <input type="submit" class="btn btn-success btnColor-expenses" value="Upload" />
    <label id="fileName"></label>
    <span class="uploaded-file">@Html.Raw(ViewBag.Message)</span>
</form>
<table class="table table-text">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.model.expenseName)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.model.price)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.model.isStandard)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.model.date)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model.ListModel)
        {
            <tr>
                <td>
                    @Html.DisplayFor(modelItem => item.expenseName)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.price)
                </td>
                <td>
                    <input type="checkbox" class="checkbox-isStandard" checked="@item.isStandard" />
                </td>
                <td>
                    @if (item.date.HasValue)
                    {
                        DateTime? datetimeToDate = @item.date;
                        string FormattedDate = datetimeToDate.Value.ToString("dd-MM-yyyy");
                        <label>@FormattedDate</label>

                    }
                </td>
                <td>
                    <a asp-action="Edit" asp-route-id="@item.expenseId">Edit</a> |
                    <a asp-action="Details" asp-route-id="@item.expenseId">Details</a> |
                    <a asp-action="Delete" asp-route-id="@item.expenseId">Delete</a>
                </td>
            </tr>
        }
    </tbody>
</table>

Finally, You can validate your expenseName property.

You can try to add this validation js.

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.1/jquery.validate.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.11/jquery.validate.unobtrusive.min.js"></script>

enter image description here

Xinran Shen
  • 8,416
  • 2
  • 3
  • 12