Model not capturing dynamic input fields [duplicate] - asp.net

This question already has answers here:
MVC Form not able to post List of objects
(3 answers)
Closed 3 years ago.
I have a view model InvoiceViewModel and inside the InvoiceViewModel I have a property Items which is type list.
public class InvoiceViewModel
{
[Key]
public int InvoiceNumber { get; set; }
public int ClientID { get; set; }
public List<ItemsViewModel> Items { get; set; }
...
}
The ItemsViewModel model looks like this
public class ItemsViewModel
{
public string Name { get; set; }
public string UnitPrice { get; set; }
public string Quantity { get; set; }
public string TotalAmount { get; set; }
}
The following fields get added into the HTML using jQuery dynamically
<tr id="InvoiceLine-606818967">
<td class="text-left line-description">
<textarea name="Items[].Name" id="line-description-606818967" class="band autogrow" rows="1"></textarea>
</td>
<td class="text-right line-unit-cost">
<input type="number" name="Items[].UnitPrice" placeholder="0.00" min="0.00" step="0.01">
</td>
<td class="text-right line-quantity">
<input type="number" name="Items[].Quantity" placeholder="0">
</td>
</tr>
<tr id="InvoiceLine-2196849859">
<td class="text-left line-description">
<textarea name="Items[].Name" id="line-description-2196849859" class="band autogrow" rows="1"></textarea>
</td>
<td class="text-right line-unit-cost">
<input type="number" name="Items[].UnitPrice" placeholder="0.00" min="0.00" step="0.01">
</td>
<td class="text-right line-quantity">
<input type="number" name="Items[].Quantity" placeholder="0">
</td>
</tr>
<tr id="InvoiceLine-7114958211">
<td class="text-left line-description">
<textarea name="Items[].Name" id="line-description-7114958211" class="band autogrow" rows="1"></textarea>
</td>
<td class="text-right line-unit-cost">
<input type="number" name="Items[].UnitPrice" placeholder="0.00" min="0.00" step="0.01">
</td>
<td class="text-right line-quantity">
<input type="number" name="Items[].Quantity" placeholder="0">
</td>
</tr>
When the form is submitted, the following is what I see during debugging of the InvoiceViewModel
Please advise what am I doing wrong?

I don't know your error message but maybe you can write Items[iterationIndex].Quantity or item.Quantity etc. Probably when submit your form, model not know which index is mine

Related

How to implement Search Functionality

i create search bar in nav i want to search Member in nav bar and i have create bootstrap cards on home page
This is My Member Model
public class Member
{
[Key]
public int MemberId { get; set; }
[StringLength(60, MinimumLength = 3)]
public string? Name { get; set; }
public string? Gender { get; set; }
public DateTime DOB { get; set; }
public string? MaritalStatus { get; set; }
public string? Address { get; set; }
public long PhoneNo { get; set; }
public string? Skills { get; set; }
public string? Hobbies { get; set; }
public string? JobTitle { get; set; }
public string? Technology { get; set; }
public string? ImageName { get; set; }
public string? ImageLocation { get; set; }
public Team? Team { get; set; }
public ICollection<TeamMember>? TeamMembers { get; set; }
public ICollection<ProjectMember>? ProjectMembers { get; set; }
}
This is my View of search Bar
<form class="d-none d-md-inline-block form-inline ms-auto me-0 me-md-3 my-2 my-md-0">
<div class="input-group">
<input class="form-control" type="text" placeholder="Search for..." aria-label="Search" aria-describedby="btnNavbarSearch" />
<button class="btn btn-primary" id="btnNavbarSearch" type="button">Search</button>
</div>
</form>
I create search bar in nav I want to search Member in nav bar and I have create bootstrap cards on home page
Though your given code is not adequate to provide a complete example
for your scenario, you could implement a search option using ViewData functionality roughly by following the below steps.
How It works
By default, we will load all the members on our Index page. Because
at the beginning search key will be empty. So we have set the conditional for that. After loading all the member List now we can search. When we enter any search key it will submit the value to
the controller using ViewData["CurrentFilter"] and finally will
search into the database by that search key and return the view
back.
Controller
public IActionResult Index( string searchString)
{
ViewData["CurrentFilter"] = searchString;
var members = from mem in _context.Members
select mem;
if (!String.IsNullOrEmpty(searchString))
{
members = members.Where(m => m.Name.Contains(searchString)
|| m.Gender.Contains(searchString));
return View(members);
}
var memberList = _context.Members.ToList();
return View(memberList);
}
Note: Currently, we have implemented searching on Member Name and Gender you can extend to other properties as well by simply adding
|| or Like this way:
members = members.Where(m => m.Name.Contains(searchString)
|| m.Gender.Contains(searchString || any Other Fields));
View
#model IEnumerable<DotNet6MVCWebApp.Models.Member>
#{
ViewData["Title"] = "Index";
}
<div class="form-row">
<table>
<tr>
<td>
<a asp-action="CreateMemberView" class="btn btn-success">Create New</a>
</td>
</tr>
</table>
</div>
<table class="table">
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model.Name)
</th>
<th>
#Html.DisplayNameFor(model => model.Gender)
</th>
<th>
#Html.DisplayNameFor(model => model.DOB)
</th>
<th>
#Html.DisplayNameFor(model => model.ImageName)
</th>
<th>
Member Details
</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Gender)
</td>
<td>
#Html.DisplayFor(modelItem => item.DOB)
</td>
<td>
<img src="~/ImageName/Cover/#item.ImageName"
class="rounded-square"
height="50" width="75"
style="border:1px"
asp-append-version="true" accept="image/*" />
</td>
<td>
<a asp-action="Details" class="btn btn-primary" asp-route-memberId="#item.MemberId">Details</a> | <a asp-action="EditMember" class="btn btn-warning" asp-route-memberId="#item.MemberId">Edit</a>
</td>
</tr>
}
</tbody>
</table>
Nav Bar
<li class="nav-item">
<div class="form-row">
<table>
<tr>
<form method="get" action="/YourController/Index">
<td style="padding-right:940px">
</td>
<td>
<input class="form-control" type="text" placeholder="Search for..." name="SearchString" value="#ViewData["CurrentFilter"]" aria-label="Search" aria-describedby="btnNavbarSearch" />
</td>
<td>
<input type="submit" value="Search" class="btn btn-primary" />
</td>
</form>
</tr>
</table>
</div>
Output
If you need any further assistance on this, please see our official document here.

Passing List of Model Class From A View to A Controller in Asp.NetCore

i'm currently trying to post a a list of ModelClass(FileInfo) to my controller, but the submit doesn't seem to call the controller at all,even when i debug, the process does not enter my Controller Action, below is my View Code(claimdocumentform.cshtml),
#model IList<PIBSSBus.DomainModels.FileInfo>
<form method="POST" enctype="multipart/form-data">
<table class="table table-striped table-hover table-bordered" id="sample_editable_1">
<thead>
<tr>
<th> Document</th>
<th> Submitted </th>
<th> Date Submitted </th>
<th> # </th>
</tr>
</thead>
<tbody>
<tr>
<td> <input type="text" placeholder="title" asp-for="#Model[0].Title" value="enter"> </td>
<td><input type="text" placeholder="Description" asp-for="#Model[0].Description" value="enter1"> </td>
<td> </td>
<td>
<input type="file" class="" asp-for="#Model[0].File" value="Upload">
</td>
</tr>
<tr>
<td> <input type="text" placeholder="title" asp-for="#Model[1].Title" value="enter2"> </td>
<td><input type="text" placeholder="Description" asp-for="#Model[1].Description" value="enter3"> </td>
<td> </td>
<td>
<input type="file" class="" asp-for="#Model[1].File" value="Upload">
</td>
</tr>
#* #Html.AntiForgeryToken();*#
</tbody>
</table>'
<button type="submit" asp-controller="Claims" asp-action="Wazobia" class="btn green button-submit">
Submit
<i class="fa fa-check"></i>
</button>
</form>
This is my controller
public IActionResult claimdocumentform()
{
//PIBSSBus.DomainModels.FileInfo something = new PIBSSBus.DomainModels.FileInfo();
// IList<PIBSSBus.DomainModels.FileInfo> filess =new List<PIBSSBus.DomainModels.FileInfo>();
return View();
}
// [ValidateAntiForgeryToken]
[HttpPost]
public IActionResult Wazobia(IList<PIBSSBus.DomainModels.FileInfo> fam)
{
return View();
}
this is my model class
public class FileInfo
{
public string Title { get; set; }
public string Description { get; set; }
[DataType(DataType.Upload)]
public IFormFile File { get; set; }
}
The issue i am trying to pass the list of FileInfo class containing two rows to my controller action Wazobia but it doesn't call the action at all, pls help
I firstly tested the code in asp.net core 3.0 and it works well(You could also try this). Then I tried in asp.net core 2.2 MVC and it does not work like yours.
Then I find that it is a bug in asp.net core 2.2 and is fixed in asp.net core 3.0,refer to https://github.com/dotnet/core/issues/2523#issuecomment-481120637
A workaround is that you receive the files outside of the model
public class FileInfoVM
{
public string Title { get; set; }
public string Description { get; set; }
}
public class FileInfo
{
public string Title { get; set; }
public string Description { get; set; }
public IFormFile File { get; set; }
}
View:
<tbody>
<tr>
<td> <input type="text" placeholder="title" asp-for="#Model[0].Title" value="enter"> </td>
<td><input type="text" placeholder="Description" asp-for="#Model[0].Description" value="enter1"> </td>
<td> </td>
<td>
<input type="file" class="" name="Files">
</td>
</tr>
<tr>
<td> <input type="text" placeholder="title" asp-for="#Model[1].Title" value="enter2"> </td>
<td><input type="text" placeholder="Description" asp-for="#Model[1].Description" value="enter3"> </td>
<td> </td>
<td>
<input type="file" class="" name="Files">
</td>
</tr>
</tbody>
Action;
[ValidateAntiForgeryToken]
[HttpPost]
public IActionResult Wazobia(IList<PIBSSBus.DomainModels.FileInfoVM> fam,List<IFormFile> Files)
{
List<FileInfo> fileInfos = new List<FileInfo>();
for(int i= 0; i< Files.Count; i++)
{
fileInfos.Add(new FileInfo()
{
Title = fam[i].Title,
Description = fam[i].Description,
File = Files[i]
});
}
// save fileInfos
return View();
}

Showing a List in a View then passing it to a HttpPost

Hi this is very simple but is not working for me.
This is my View:
#model iLeadsTest.ViewModels.SaveViewModel
#using (Html.BeginForm("Save", "Clients", FormMethod.Post))
{
<div>
<table class="table">
<thead>
<tr>
<th>Pin Number</th>
<th>First Name</th>
<th>Last Name</th>
</tr>
</thead>
<tbody>
#for (int i = 0; i < Model.FileClasses.Count; i++)
{
<tr>
<td>#Html.DisplayFor(m => m.FileClasses[i].PinNumberProperty)
</td>
<td>#Html.DisplayFor(m => m.FileClasses[i].FirstNameProperty)
</td>
<td>#Html.DisplayFor(m => m.FileClasses[i].LastNameProperty)
</td>
</tr>
}
</tbody>
</table>
</div>
#Html.HiddenFor(m=>m.FileName);
<input class="btn btn-primary" type="submit" name="Submit" id="Submit" value="Upload" />
}
My ViewModel:
public class SaveViewModel
{
public string FileName { get; set; }
public List<Clients> FileClasses { get; set; }
}
Why when I go to the method in my controller I'm getting an empty list?

When using viewmodel can not convert list to entities

Hi I use viewmodel and in my controller can not convert list to entity.
Here is my viewmodel:
namespace SmartAdvertising.Backend.Models
{
public class DashboardModel
{
public Advert allAdvert { get; set; }
public Advert allAdvert2 { get; set; }
}
}
Controller:
public ActionResult Dashboard()
{
string email_uzivatele = (String)Session["email"];
DashboardModel adverts = new DashboardModel();
List<Advert> time = AdvertServiceLayer.Instance.SelectAllWith(email_uzivatele, "Reklama na určitou dobu");
List<Advert> price = AdvertServiceLayer.Instance.SelectAllWith(email_uzivatele, "Reklama na určitou částku");
adverts.allAdvert = time;
adverts.allAdvert2 = price;
return View(adverts);
}
View:
#model IList<SmartAdvertising.Backend.Models.DashboardModel>
#{
ViewBag.Title = "Dashboard";
}
<h1>#ViewBag.Title</h1>
<div class="row">
<div class="col-md-12 col-sm-12">
<h2>Všechny reklamy na určitou dobu</h2>
<table class="table-hover" style="width:100%">
<tr>
<th>
Název reklamy
</th>
<th>
Typ reklamy
</th>
<th>
Cena reklamy
</th>
<th>
Email uživatele
</th>
<th>
Datum začátku
</th>
<th>
Datum konce
</th>
<th>
</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#item.nazev_reklamy
</td>
<td>
#item.typ_reklamy
</td>
<td>
#item.cena_reklamy
</td>
<td>
#item.email_uzivatele
</td>
<td>
#item.datumz
</td>
<td>
#item.datumk
</td>
</tr>
}
</table>
</div>
<div class="col-md-12 col-sm-12">
<h2>Všechny reklamy na určitou částku</h2>
<table class="table-hover" style="width:100%">
<tr>
<th>
Název reklamy
</th>
<th>
Typ reklamy
</th>
<th>
Cena reklamy
</th>
<th>
Email uživatele
</th>
<th>
</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#item.nazev_reklamy
</td>
<td>
#item.typ_reklamy
</td>
<td>
#item.cena_reklamy
</td>
<td>
#item.email_uzivatele
</td>
</tr>
}
</table>
</div>
<div class="col-md-12 col-sm-12">
</div>
</div>
But in view it is giving this error:
Error 1 foreach statement cannot operate on variables of type 'SmartAdvertising.Backend.Models.DashboardModel'
because 'SmartAdvertising.Backend.Models.DashboardModel' does not contain a public definition for 'GetEnumerator'
And in controller these two lines:
adverts.allAdvert = time;
adverts.allAdvert2 = price;
are giving me this error :
Error 3 Cannot implicitly convert type 'System.Collections.Generic.List<SmartAdvertising.Entities.Advert>'
to 'SmartAdvertising.Entities.Advert'
Properties in model must be of type List:
public class DashboardModel
{
public List<Advert> allAdvert { get; set; }
public List<Advert> allAdvert2 { get; set; }
}
Additionally, you have to iterate over the properties of the model:
#foreach (var item in Model.allAdvert)

refactor Html.LabelFor, EditorFor, ValidationMessageFor into partial view

I want to refactor view form code to avoid copyPaste. But it's not working. Razor don't allow to write
#model System.Linq.Expressions.Expression<Func<TModel, TValue>> expression
and
#Html.Partial("Item", model => model.EmpName)
Old code, that working:
<tr>
<td class="editor-label" style="border: 0;">
#Html.LabelFor(model=>model.EmpName)
</td>
<td class="editor-field" style="border: 0">
#Html.EditorFor(model=>model.EmpName)
#Html.ValidationMessageFor(model=>model.EmpName)
</td>
</tr>
<tr>
<td class="editor-label" style="border: 0;">
#Html.LabelFor(model=>model.Email)
</td>
<td class="editor-field" style="border: 0;">
#Html.EditorFor(model=>model.Email)
#Html.ValidationMessageFor(model=>model.Email)
</td>
</tr>
After refactoring not working:
Item.cshtml:
#model System.Linq.Expressions.Expression<Func<TModel, TValue>> expression
<tr>
<td class="editor-label" style="border: 0;">
#Html.LabelFor(expression)
</td>
<td class="editor-field" style="border: 0">
#Html.EditorFor(expression)
#Html.ValidationMessageFor(expression)
</td>
</tr>
}
New code:
#Html.Partial("Item", model => model.EmpName)
#Html.Partial("Item", model => model.Email)
How to make it work?
You can write your own HtmlHelper extension method, something like:
public static MvcHtmlString MyEditFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression)
{
var group = new TagBuilder("div");
group.InnerHtml = html.LabelFor(expression).ToString();
//more formatting and controls here
return MvcHtmlString.Create(group.ToString());
}
This will allow you to write:
#Html.MyEditFor(m => m.Name)
The drawback of this approach is that you won't be able to inline HTML because this is not a view. However it does allow you to set up a standard control layout.
The solution was found. Thanx to #Paul.
Item.cshtml:
#model MyClientCustomValidation.Models.LabelEditorValidation
<tr>
<td class="editor-label" style="border: 0;">
#Model.Label
</td>
<td class="editor-field" style="border: 0">
#Model.Editor
#Model.Validation
</td>
</tr>
Which has model:
public class LabelEditorValidation
{
public MvcHtmlString Label { get; set; }
public MvcHtmlString Editor { get; set; }
public MvcHtmlString Validation { get; set; }
}
And inside form:
#Html.MyEditFor(model=>model.EmpName)
#Html.MyEditFor(model=>model.Email)
Where MyEditorFor is
public static MvcHtmlString MyEditFor<TModel, TProperty>(this HtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression)
{
return html.Partial("Item", new LabelEditorValidation() { Label = html.LabelFor(expression), Editor = html.EditorFor(expression), Validation = html.ValidationMessageFor(expression) });
}

Resources