MVC Model binding on multiple edit - c#

I have the following view model
public class ResourceProjectedCapacitiesModel
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Location { get; set; }
public string Manager { get; set; }
public string Role { get; set; }
public string Project { get; set; }
public decimal Capacity { get; set; }
public System.DateTime Date { get; set; }
}
In my GET action i display all these in the view.
[HttpPost]
public ActionResult Index(List ResourceProjectedCapacitiesModel> list)
{
foreach (ResourceProjectedCapacitiesModel item in list)
{
....
}
....
}
In the view there is the following editor but when the form is submitted the binding doesnt work, list is null
#Html.EditorFor(modelItem => item.Capacity)
#Html.HiddenFor(modelItem => item.ID)
#model List<CapacityPlanner.ViewModels.ResourceProjectedCapacitiesModel>
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
#using(Html.BeginForm())
{
<table class="table">
<tr>
<th>
First Name
</th>
<th>
Last Name
</th>
<th>
Location
</th>
<th>
Manager
</th>
<th>
Role
</th>
<th>
Project
</th>
<th>Apr</th>
<th>May</th>
<th>Jun</th>
<th>Jul</th>
<th>Aug</th>
<th>Sep</th>
<th>Oct</th>
<th>Nov</th>
<th>Dec</th>
<th>Jan</th>
<th>Feb</th>
<th>Mar</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.FirstName)
</td>
<td>
#Html.DisplayFor(modelItem => item.LastName)
</td>
<td>
#Html.DisplayFor(modelItem => item.Location)
</td>
<td>
#Html.DisplayFor(modelItem => item.Manager)
</td>
<td>
#Html.DisplayFor(modelItem => item.Role)
</td>
<td>
#Html.DisplayFor(modelItem => item.Project)
</td>
#for(var i = 1; i <= 12; i++)
{
<td>
#if(item.Date.Month == i)
{
#Html.EditorFor(modelItem => item.Capacity)
}
</td>
}
<td>
#Html.HiddenFor(modelItem => item.ID)
</td>
</tr>
}
</table>
<input type="submit" value="Save" class="btn btn-default" />
}
I have inserted the code of my view

You should use for instead of foreach.
#for (int i = 0; i < Model.Items.Count; i++)
{
#Html.EditorFor(model => Model.Items[i].Amount)
#Html.HiddenFor(model => Model.Items[i].Amount)
}
Then all filled will get different id.

Related

Get the Child Class Propert in Parent class

I had a two controllers in my application with database first approach
SampCutReqMaster.CS
public partial class SampCutReqMaster
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public SampCutReqMaster()
{
this.SamCutAssignmentMasters = new HashSet<SamCutAssignmentMaster>();
}
public decimal SampCutreqID { get; set; }
public Nullable<decimal> BuyerID { get; set; }
public Nullable<decimal> PatternRefID { get; set; }
public Nullable<decimal> PatternStyleID { get; set; }
public Nullable<decimal> SampleTypeID { get; set; }
public virtual BuyerMaster BuyerMaster { get; set; }
public virtual PatternStyle PatternStyle { get; set; }
public virtual PatterRefMaster PatterRefMaster { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<SamCutAssignmentMaster> SamCutAssignmentMasters { get; set; }
public virtual SampleType SampleType { get; set; }
}
and The next is
SamCutAssignmentMaster
public partial class SamCutAssignmentMaster
{
public decimal CutAssignID { get; set; }
public decimal SampCutreqID { get; set; }
public System.DateTime ReceivedDate { get; set; }
public string ReceivedBy { get; set; }
public Nullable<decimal> PatternMasterID { get; set; }
public virtual PatternMaster PatternMaster { get; set; }
public virtual SampCutReqMaster SampCutReqMaster { get; set; }
}
I had created a controller with a index view for SampCutReqMaster
public class SampCutReqMastersController : Controller
{
private ArtEntities db = new ArtEntities();
// GET: SampCutReqMasters
public ActionResult Index()
{
var sampCutReqMasters = db.SampCutReqMasters.Include(s => s.BuyerMaster).Include(s => s.PatternStyle).Include(s => s.PatterRefMaster).Include(s => s.SampleType).Include(s=>s.SamCutAssignmentMasters);
var sampCutReqMasterssort = sampCutReqMasters.ToList().OrderByDescending(a => a.AddedDate);
return View(sampCutReqMasterssort.ToList());
}
}
I want to get the "receivedBy" of SamcutAssignmentMaster(child class) in my view of SamCutReqMaster (Parent).But there may be no data in SamcutAssignmentMaster relavant to SamCutReqMaster (FK is SamCutReqID)
In Index view Below I need to access SamcutAssignmentMaster.receivedBy
#model IEnumerable<WebArtSampler.Models.SampCutReqMaster>
#{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Index</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.ReqNum)
</th>
<th>
#Html.DisplayNameFor(model => model.Fabric)
</th>
<th>
#Html.DisplayNameFor(model => model.SampleRequiredDate)
</th>
<th>
#Html.DisplayNameFor(model => model.AddedDate)
</th>
<th>
#Html.DisplayNameFor(model => model.AddedBy)
</th>
<th>
#Html.DisplayNameFor(model => model.BuyerMaster.BuyerName)
</th>
<th>
#Html.DisplayNameFor(model => model.PatternStyle.StyleName)
</th>
<th>
#Html.DisplayNameFor(model => model.PatterRefMaster.PatterRefNum)
</th>
<th>
#Html.DisplayNameFor(model => model.SampleType.SampleType1)
</th>
<th>
#Html.DisplayNameFor(model => model.SizeDetail)
</th>
<th>
#Html.DisplayNameFor(model => model.Qty)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.ReqNum)
</td>
<td>
#Html.DisplayFor(modelItem => item.Fabric)
</td>
<td>
#Html.DisplayFor(modelItem => item.SampleRequiredDate)
</td>
<td>
#Html.DisplayFor(modelItem => item.AddedDate)
</td>
<td>
#Html.DisplayFor(modelItem => item.AddedBy)
</td>
<td>
#Html.DisplayFor(modelItem => item.BuyerMaster.BuyerName)
</td>
<td>
#Html.DisplayFor(modelItem => item.PatternStyle.StyleName)
</td>
<td>
#Html.DisplayFor(modelItem => item.PatterRefMaster.PatterRefNum)
</td>
<td>
#Html.DisplayFor(modelItem => item.SampleType.SampleType1)
</td>
<td>
#Html.DisplayFor(modelItem => item.SizeDetail)
</td>
<td>
#Html.DisplayFor(modelItem => item.Qty)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.SampCutreqID }) |
#Html.ActionLink("Details", "Details", new { id=item.SampCutreqID }) |
#Html.ActionLink("Delete", "Delete", new { id=item.SampCutreqID })
</td>
</tr>
}
</table>
Ok. You can't access the properties whilst your type is still as the interface ICollection. You need to cast it to a concrete type before you can access the properties of the collection via an index.
#for (var adCounter = 0; adCounter <= (Model.SamCutAssignmentMasters.Count - 1); adCounter++)
{
#Html.DisplayFor(x => ((List<SamCutAssignmentMaster>)x.SamCutAssignmentMasters)[adCounter].ReceivedBy)
}
Cast the type to a list as per the above and you should see the properties.
NOTE: You are using DisplayFor in your view. If you intend to post the contents of this view the model will not bind unless you have a hidden control for each item or you switch to TextBoxFor. You will also need the for (Counter) syntax as well to bind to items in collections.
Hope that helps.

ASP.NET MVC Core passing a value from dropdownlist in a view to a controller

I'm struggling with (perhaps) a simple challenge.
I have the following classes:
public class Product
{
public int ID { get; set; }
[Display(Name ="Product Name")]
public string Name { get; set; }
public string Description { get; set; }
public decimal Price { get; set; }
public int? CategoryID { get; set; }
public virtual Category Category { get; set; }
}
public class Category
{
public int ID { get; set; }
[Display(Name = "Category Name")]
public string Name { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
To store the data I'm using EF Core
public class StoreContext : DbContext
{
public StoreContext(DbContextOptions<StoreContext> options) : base(options) { }
public DbSet<Category> Categories { get; set; }
public DbSet<Product> Products { get; set; }
}
And in my ProductsController I have the following GET method:
public async Task<IActionResult> Index(int category, string search)
{
var storeContext = _context.Products.Include(p => p.Category) as IQueryable<Product>;
if (category != 0)
{
storeContext = storeContext.Where(p => p.Category.ID == category);
}
if (search != null)
{
storeContext = storeContext.Where(p => p.Category.Name == search);
}
var categories = storeContext.OrderBy(p => p.Category.Name).Select(p => p.Category.Name).Distinct();
if (!String.IsNullOrEmpty(search))
{
storeContext = storeContext.Where(p => p.Name.Contains(search)||
p.Description.Contains(search) ||
p.Category.Name.Contains(search));
ViewBag.Search = search;
}
ViewBag.Category = new SelectList(categories);
return View(await storeContext.ToListAsync());
}
My Index.cshtml looks like as follows:
#model IEnumerable<BabyStore.Models.Product>
#{
ViewData["Title"] = "Index";
}
<h2>Index</h2>
<p>
<a asp-action="Create">Create New</a>
</p>
<form asp-action="Index" asp-controller="Products" method="get">
<div class="form-group">
<table>
<tr>
<td>
<label>Filtered by Category: </label>
</td>
<td>
<select class="form-control" asp-items="ViewBag.Category">
<option value="">-- Select Category --</option>
</select>
</td>
<td>
<input type="submit" value="Filter" id="search"/>
<input type="hidden" name="search" id="search"/>
</td>
</tr>
</table>
</div>
</form>
<br />
<table class="table">
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model.Description)
</th>
<th>
#Html.DisplayNameFor(model => model.Name)
</th>
<th>
#Html.DisplayNameFor(model => model.Category.Name)
</th>
<th>
#Html.DisplayNameFor(model => model.Price)
</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Description)
</td>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Category.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Price)
</td>
<td>
<a asp-action="Edit" asp-route-id="#item.ID">Edit</a> |
<a asp-action="Details" asp-route-id="#item.ID">Details</a> |
<a asp-action="Delete" asp-route-id="#item.ID">Delete</a>
</td>
</tr>
}
</tbody>
</table>
Pushing the "Filter" button should cause refreshing the View and filtering by Category.Name
But, unfortunately it does not work.
In my browser I read
http://localhost:53827/Products?search=
So no parameter has been passed to search.
If I enter by hand
http://localhost:53827/Products?search=clothes
it works perfectly
In the Startup.cs I have the following Route
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
Any idea how to solve it?

Using Checkbox in mvc

public class ProdList
{
public int ProdId { get; set; }
[DisplayName("Name")]
public string ProdName { get; set; }
[DisplayName("Price")]
public double? ProdPrice { get; set; }
[DisplayName("Description")]
public string ProdDesc { get; set; }
[DisplayName("Image")]
public string ImageVal { get; set; }
[DisplayName("Category Name")]
public string CateName { get; set; }
}
I have this class in my ProductListController. Using the following Action, I am populating my above class and passing it to the View.
public ActionResult ProdList()
{
Context con = new Context();
var prodlist = from p in con.Proddb join c in con.Catdb on p.CategoryID equals c.CategoryID select new ProdList() { ProdId=p.ProductID, ProdName = p.ProductName, ProdPrice = p.UnitPrice, ProdDesc=p.Description, CateName=c.CategoryName, ImageVal= "~/Image/"+p.ImagePath };
List<ProdList> prodlst = prodlist.ToList<ProdList>();
var categories = con.Catdb.ToList<Category>();
ViewBag.Categories = categories;
return View("ProdListView", prodlst);
}
My ProdListView includes the following list where I am printing the data:
#using (Html.BeginForm("checkboxhandle", "ProductList"))
{
#Html.AntiForgeryToken()
<table class="table table-condensed" id="prodtbl">
<tr>
<th>
#Html.DisplayNameFor(model => model.ImageVal)
</th>
<th>
#Html.DisplayNameFor(model => model.ProdName)
</th>
<th>
#Html.DisplayNameFor(model => model.ProdPrice)
</th>
<th>
#Html.DisplayNameFor(model => model.ProdDesc)
</th>
<th>
#Html.DisplayNameFor(model => model.CateName)
</th>
<th>
Check to Add
</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
<img src="#Url.Content(#Html.DisplayFor(modelItem => item.ImageVal).ToString())" height="150" width="150">
</td>
<td>
#Html.DisplayFor(modelItem => item.ProdName)
</td>
<td>
#Html.DisplayFor(modelItem => item.ProdPrice)
</td>
<td>
#Html.DisplayFor(modelItem => item.ProdDesc)
</td>
<td>
#Html.DisplayFor(modelItem => item.CateName)
</td>
<td>
#*Checkbox*#
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id = item.ProdId }) |
#Html.ActionLink("Details", "Details", new { id = item.ProdId }) |
</td>
</tr>
}
</table>
<div class="form-group" style="text-align:center">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Add to the Cart" class="btn btn-default" />
</div>
</div>
}
Following is the action method I am invoking once the submit button is clicked.
[HttpPost]
public ActionResult checkboxhandle(ProdList pdl)
{
/*Logic to get the checked items from the list in the view*/
}
I tried various methods to include checkbox and retrieve the checked items back in my controller but it didn't help. I'm really confused how to proceed. Please help me.
First of all add a bool property to your model/viewmodel:
public bool SelectedProduct {get;set;}
and then you need to do a for loop for the List<T>, so that model binder could populate items back when form posted like:
#for(int i=0; i< Model.Count; i++)
{
<tr>
<td>
<img src="#Url.Content(#Html.DisplayFor(modelItem => Model[i].ImageVal).ToString())" height="150" width="150">
</td>
<td>
#Html.DisplayFor(modelItem => Model[i].ProdName)
</td>
<td>
#Html.DisplayFor(modelItem => Model[i].ProdPrice)
</td>
<td>
#Html.DisplayFor(modelItem => Model[i].ProdDesc)
</td>
<td>
#Html.DisplayFor(modelItem => Model[i].CateName)
</td>
<td>
#Html.CheckBoxFor(modelItem => Model[i].SelectedProduct)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id = Model[i].ProdId }) |
#Html.ActionLink("Details", "Details", new { id = Model[i].ProdId }) |
</td>
</tr>
}

Enable boolean and enter text in view then pass back to controller - MVC

I have a view that displays a boolean (currently defaulted to 0) in a box format in the view that I cannot check to activate as true and also want to enter text in the result field to pass back to the controller and save both changes to a table. Can someone please explain what I have to do to allow this functionality to work please.
Controller code
public ActionResult P1A1Mark()
{
List<MarkModel> query = (from row in db.submits
where row.assignment_no.Equals("1") && row.group_no == 1
group row by new { row.assignment_no, row.student_no, row.student.firstname, row.student.surname } into g
select new MarkModel
{
student_no = g.Key.student_no,
student_surname = g.Key.surname,
student_firstname = g.Key.firstname
}
).ToList();
return View(query);
}
View
#model IEnumerable<MvcApplication2.Models.MarkModel>
#{
ViewBag.Title = "P1A1Mark";
}
<h2>Mark Student Assignments</h2>
<table>
<tr>
<th>
#Html.DisplayNameFor(model => model.student_no)
</th>
<th>
#Html.DisplayNameFor(model => model.student_surname)
</th>
<th>
#Html.DisplayNameFor(model => model.student_firstname)
</th>
<th>
#Html.DisplayNameFor(model => model.submitted)
</th>
<th>
#Html.DisplayNameFor(model => model.result)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.student_no)
</td>
<td>
#Html.DisplayFor(modelItem => item.student_surname)
</td>
<td>
#Html.DisplayFor(modelItem => item.student_firstname)
</td>
<td>
#Html.DisplayFor(modelItem => item.submitted)
</td>
<td>
#Html.DisplayFor(modelItem => item.result)
</td>
</tr>
}
</table>
Model
public class MarkModel
{
public string student_no { get; set; }
public string student_surname { get; set; }
public string student_firstname { get; set; }
public string assignment_no { get; set; }
public bool submitted { get; set; }
public string result { get; set; }
public Nullable<int> group_no { get; set; }
}
Create an EditorTemplate for type of MarkModel.
In /Views/Shared/EditorTemplates/MarkModel.cshtml
#model MvcApplication2.Models.MarkModel
<tr>
<td>#Html.DisplayFor(m => m.student_no)</td>
<td>#Html.DisplayFor(m => m.student_surname)</td>
<td>#Html.DisplayFor(m => m.student_firstname)</td>
<td>#Html.CheckBoxFor(m => m.submitted)</td>
<td>#Html.TextBoxFor(m => m.result)</td>
</tr>
and in the main view
#model IEnumerable<MvcApplication2.Models.MarkModel>
#using (Html.BeginForm())
{
<table>
<thead>
// add your th elements
</thead>
<tbody>
#Html.EditorFor(m => m)
<tbody>
</table>
<input type="submit" ../>
}
and create a method to post back to
[HttpPost]
public ActionResult P1A1Mark(IEnumerable<MarkModel>model)
Alternatively you can use a for loop in the view (the model must be IList<T>)
for(int i = 0; i < Model.Count; i++)
{
....
#Html.CheckBoxFor(m => m[i].submitted)
}

Getting the UserName of an ApplicationUser into a view

I am trying to display the name of an author of a question but unlike the other properties of the model, the ApplicationUser.UserName property does not show in the view (it's just blank). I am using Entity Framework, MVC 5 and Razor.
My Question model:
public class Question
{
public int QuestionID { get; set; }
public string Title { get; set; }
public ApplicationUser Author { get; set; }
public string Description { get; set; }
[DisplayName("Created")]
public DateTime CreationDateTime { get; set; }
public int Rating { get; set; }
}
My QuestionController Index action:
// GET: Questions
public ActionResult Index()
{
return View(db.Questions.ToList());
}
And the view:
#model IEnumerable<Waegogi.Models.Question>
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Title)
</th>
<th>
#Html.DisplayNameFor(model => model.Author.UserName)
</th>
<th>
#Html.DisplayNameFor(model => model.Description)
</th>
<th>
#Html.DisplayNameFor(model => model.CreationDateTime)
</th>
<th>
#Html.DisplayNameFor(model => model.Rating)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Title)
</td>
<td>
#Html.DisplayFor(modelItem => item.Author.UserName)
</td>
<td>
#Html.DisplayFor(modelItem => item.Description)
</td>
<td>
#Html.DisplayFor(modelItem => item.CreationDateTime)
</td>
<td>
#Html.DisplayFor(modelItem => item.Rating)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.QuestionID }) |
#Html.ActionLink("Details", "Details", new { id=item.QuestionID }) |
#Html.ActionLink("Delete", "Delete", new { id=item.QuestionID })
</td>
</tr>
}
</table>
Where am I going wrong?
You should be able to solve this by explicitly loading your relationships in your controller call:
public ActionResult Index()
{
return View(db.Questions.Include(q => q.ApplicationUser).ToList());
}

Categories