Reputation: 4332
Tools & Pltaform: ASP.NET Core MVC 3.1, Visual Studio 2019, Win 10
Update: It's an Upsert view. I'm trying to loop through properties of the model in Razor page to generate labels and input HTML elements inside the form tag, but the rendered HTML has the "Item" (the name of the variable in foreach) as the value for id, name and *for HTML attributes.
Rendered HTML in browser
<div class="form-group row">
<div class="col-3">
<label for="item">item</label>
</div>
<div class="col-6">
<input class="form-control" type="text" id="item" name="item" value="Author">
<span class="text-danger field-validation-valid" data-valmsg-for="item" data-valmsg-replace="true"></span>
</div>
</div>
HTML displayed in the browser
Razor foreach loop producing the HTML
@{ var propList = new string[] { "Name", "Author", "ISBN" };}
@foreach (var item in propList)
{
<div class="form-group row">
<div class="col-3">
<label asp-for="@item"></label>
</div>
<div class="col-6">
<input asp-for="@item" class="form-control" />
<span asp-validation-for="@item" class="text-danger"></span>
</div>
</div>
}
Can someone explain why this is happening?
Update:
Based on '@Ben Sampica' suggestions, I have made these changes,
How would my view look like, I mean I remember using Model List for a table like view (I have used ASP.NET MVC a few years back, now jumping to .net core), here are the changes I have made,
View:
@foreach (var book in Model.Books)
{
<div class="form-group row">
<div class="col-3">
<label asp-for="@book.Name"></label>
</div>
<div class="col-6">
<input asp-for="@book.Name" class="form-control" />
<span asp-validation-for="@book.Name" class="text-danger"></span>
</div>
</div>
}
I'm using the exact View model as you described "BooksViewModel"
I haven't stated that in the question before but to be clear I'm creating an Upsert view, so now the ViewModel is a list although I'm only concerned with a single record at a time, the view model is empty on create action because there is no data in the list.
Upvotes: 2
Views: 2194
Reputation: 3402
If you look at the Microsoft Docs on ASP.NET Core Tag Helpers you'll see that asp-for
on a <label>
will grab the [Display]
attribute off the property (if present). As a backup, it'll use the property name. In your case, you're passing in an string collection, so let's dive into what the Razor is seeing
PropList[]
{
string item = "Name"
string item = "Author"
string item = "ISBN"
}
When this is iterated on, there's no Display
property, so from our rules above, what's the convention? Using the property name (which you named item
in your iterator).
I think what you're trying to do might be better accomplished in a dedicated model class such as:
public class BookViewModel
{
public string Name { get; set; }
public string Author { get; set; }
public string ISBN { get; set; }
}
then on your view
@using BookViewModel
<form>
<div class="form-group row">
<div class="col-3">
<label asp-for="@Model.Name"></label>
</div>
<div class="col-6">
<input asp-for="@Model.Name" class="form-control" />
<span asp-validation-for="@Model.Name" class="text-danger"></span>
</div>
</div>
<div class="form-group row">
<div class="col-3">
<label asp-for="@Model.Author"></label>
</div>
<div class="col-6">
<input asp-for="@Model.Author" class="form-control" />
<span asp-validation-for="@Model.Author" class="text-danger"></span>
</div>
</div>
<div class="form-group row">
<div class="col-3">
<label asp-for="@Model.ISBN"></label>
</div>
<div class="col-6">
<input asp-for="@Model.ISBN" class="form-control" />
<span asp-validation-for="@Model.ISBN" class="text-danger"></span>
</div>
</div>
<button type="submit">Submit</button>
</form>
Update Response:
Below response is from the chat conversation b/w the op and the Answer poster
If I was making this, I'd just make two actions (Add & Edit) and share a partial view for the form inputs so that the code is reused as much as possible - while not getting too clever with the solution with this upsert. That's just my opinion. I think this was part of why I was having trouble understanding what you were looking for.
I also wouldn't use things like a reflection to write HTML. If you want to reuse the general layout of a type of input (like a textbox), at most I'd make a view component and pass the property. I would not @foreach an entire model - how would you determine what a dropdown is? What about a checkbox? You'll get yourself in a really bad spot quickly with clever solutions that are unmaintainable.
Finally,
Upvotes: 1