Model is not populated during form post ASP.NET MVC - asp.net-mvc

I am learning ASP.NET MVC. I am trying to catch model data on form post, but model is showing as null.
Here is my Model
public class SampleModel
{
public int ID { get; set; }
public string Name { get; set; }
public string CRUDOperation { get; set; }
}
Here is my view
#model IEnumerable<SampleModel>
#using (Html.BeginForm("SubmitUpdateGridRow", "GridView", FormMethod.Post, new {value = "form" }))
{
#Html.AntiForgeryToken()
<table class="table table-bordered">
<tr>
<th>
#Html.Label("CRUD Actions")
</th>
<th>
#Html.DisplayNameFor(model => model.ID)
</th>
<th>
#Html.DisplayNameFor(model => model.Name)
</th>
</tr>
#foreach (SampleModel Row in Model)
{
<tr>
#if (Row.CRUDOperation == "Select")
{
<td>
#Html.ActionLink("Update", "UpdateGridRow", "GridView", Row, new { #title = "U: Update Operation of CRUD" }) |
#Html.ActionLink("Edit", "EditGridRow", "GridView", new { id = Row.BGID }, new { #title = "U: Update Operation of CRUD" }) |
#Html.ActionLink("Delete", "DeleteGridRow", "GridView", new { id = Row.BGID }, new { #title = "D: Delete Operation of CRUD" }) |
#Html.ActionLink("Details", "DetailsGridRow", "GridView", new { id = Row.BGID }, new { #title = "Form level view for details" })
</td>
<td>
#Row.ID
</td>
<td>
#Row.Name
</td>
}
else if (Row.CRUDOperation == "Edit")
{
<td>
#Html.HiddenFor(ID => Row.ID)
#Html.HiddenFor(CRUDOperation => Row.CRUDOperation)
#Row.BGID
</td>
<td>
#Html.EditorFor(Name => Row.Name)
</td>
<td>
<input type="submit" value="Update" id="UpdateSubmit" />
#Html.ActionLink("Edit", "EditGridRow", "GridView", new { id = Row.ID }, new { #title = "U: Update Operation of CRUD" }) |
#Html.ActionLink("Reset", "ResetGridRow", "GridView", new { id = Row.ID }, new { #title = "R: Reset Operation of CRUD" })
</td>
}
</tr>
#*<tr>
#if (Row.CRUDOperation == "Edit")
{
EditRow(Row);
}
else
{
DisplayRow(Row);
}
</tr>*#
//Row.CRUDOperation.Equals("Edit") ? EditRow(Row) : DisplayRow(Row);
}
</table>
}
Here is my controller
public class GridViewController : Controller
{
[HttpPost]
public ActionResult SubmitUpdateGridRow(FormCollection FC, SampleModel VM)
{
string str = (string)FC.GetValue("Row.Name").AttemptedValue;
......
}
}
I was able to get the values from form collection, but my model is coming as null.
Thanks in advance.
PS: i would like to find the solution only with server side scripting, dont want to use javascript, JQuery

In your view you are posting a collection of SampleModel however your controller only takes in a single SampleModel parameter.
First you need to change your controller to take in IEnumerable<SampleModel>.
Then in your view, you need to pass the index to the html helpers to generate the correct html for list model binding to work out of the box.
For example:
#model IList<SampleModel>
#for(var i = 0; i < Model.Count; i++)
{
#Html.HiddenFor(m => Model[i].Id)
}
Check this useful article for more on model binding lists

Put Antiforgery attribute on top of the controller action as your HTML form has Antiforgery tag.

Related

MVC DropDownList lagging

I am posting the id of a dropdownlist back to the index (index2 view). but is lagging behind. After a second time pressing Select it shows me the correct list.
http://www.jeroenchristens.be/CountriesWorld
(the first page is only for showing the complete list, after selecting from the dropdownlist,, it gets to index2, a shorter list) And then after choosing another Selection from the dropdownlist, you have to try this twice each time.
I successfully copied this from the id the value and pass this on, why is it lagging behind.
Index2 Viewpage
#using System.Collections
#using System.Web.UI.WebControls
#model IEnumerable<CVtje.Models.Countries>
<h2>Index</h2>
#using (Html.BeginForm("Index2", "CountriesWorld", new { #id = Request.Form["SelectedContinent"] }, FormMethod.Post))
{
<div class="form-group">
#Html.DropDownList("SelectedContinent",
new SelectList((IEnumerable) ViewData["continentsList"], "Continent", "Continentomschrijving"))
<button type="submit" class="btn btn-primary">Select</button>
</div>
}
<table id="countriesworld" class="table table-active table-hover">
<thead>
<tr>
<th>Vlag</th>
<th>
Code
</th>
<th>
Land
</th>
<th>Continent</th>
</tr>
</thead>
#foreach (var item in Model)
{
<tr>
<td>
<img src="#string.Format("../../images/countries/{0}.png", item.Code)" width="25" HEIGHT="15" />
</td>
<td>
#item.Code
</td>
<td>
#item.Country
#*#Html.ActionLink("Details", "Index", "ReizensDetails", new { id = item.ReizenId }, null)*#
#*|
#Html.ActionLink("Details", "Details", new { id = item.Id }) |
<button data-myprofile-id="#item.Id" class="btn-link js-delete">Delete</button>*#
</td>
<td>#item.Continents.Continentomschrijving</td>
</tr>
}
</table>
my controller:
public ActionResult Index(int? id)
{
List<Continents> continentsList = new List<Continents>();
continentsList = _context.Continents.ToList();
ViewData["continentsList"] = continentsList;
var countriesWorld = _context.Countries.OrderBy(e => e.Country).ToList();
return View(countriesWorld);
}
[HttpPost]
public ActionResult Index2(int id)
{
//return View(db.MyProfiles.ToList());
List<Continents> continentsList = new List<Continents>();
continentsList = _context.Continents.ToList();
ViewData["SelectedContinent"] = id.ToString();
ViewData["continentsList"] = continentsList;
var countriesWorld = _context.Countries.Where(e => e.Continent == id).OrderBy(e => e.Country).ToList();
return View(countriesWorld);
You have added a route value using new { #id = Request.Form["SelectedContinent"] } in your BeginForm() method.
Assuming the initial value is 0, then it generates action = "/CountriesWorld/Index2/0". Lets assume you select the option with value="1" and you now post the form. The id attribute is bound to 0 and you filter the Countries based on .Where(e => e.Continent == 0) - no where have you ever used the value of the selected option which is bound to a non-existent property named SelectedContinent.
Now you return the view and the forms action attribute is now action = "/CountriesWorld/Index2/1" (because Request.Form["SelectedContinent"] is 1). If you select the option with value="2", the same thing occurs - you ignore the value of the selected option and the filter the Countries based on .Where(e => e.Continent == 1) because the id parameter is 1.
Always bind to a model, which in your case will be
public class CountriesVM
{
public int? SelectedContinent { get; set }
public IEnumerable<SelectListItem> ContinentsList { get; set; }
public IEnumerable<Country> Countries { get; set; }
}
and in the view, strongly bind to your model (note the FormMethod.Get and the 3rd parameter in DropDownListFor())
#model CountriesVM
#using (Html.BeginForm("Index", "CountriesWorld", FormMethod.Get))
{
#Html.DropDownListFor(m => m.SelectedContinent, Model.ContinentsList, "All")
<button type="submit" class="btn btn-primary">Select</button>
}
<table ... >
....
#foreach(var country in Model.Countries)
{
....
}
</table>
and you need only one method
public ActionResult Index(int? selectedContinent)
{
var countries = _context.Countries.OrderBy(e => e.Country);
if (selectedContinent.HasValue)
{
countries = countries.Where(e => e.Continent == selectedContinent.Value);
}
continentsList = _context.Continents.Select(x => new SelectListItem
{
Value = x.Continent.ToString(),
Text = x.Continentomschrijving
});
var model = new CountriesVM
{
SelectedContinent = selectedContinent,
ContinentsList = continentsList,
Countries = countries
};
return View(model);
}
Note you might also want to consider caching the Continents to avoid repeated database calls assuming they do not change often (and invalidate the cache if their values are updated)

.NET MVC JQuery function is not getting fired in table row click event

I have table data in UI. I want to display data in a div in the same page when I click the Details link in the row. The JQuery function is not getting fired in when I click on details link in any row.
Below is my code:
Model view class:
public class ItemViewModel
{
public Item item { get; set; }
public IEnumerable<Item> items { get; set; }
}
UI Code:
#model Medhub.Models.ItemViewModel
#{
ViewBag.Title = "View";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>View</h2>
<p>
#Html.ActionLink("Create New", "CreateEdit", new { controller = "Item" }, new { #class = "btn btn-primary" })
</p>
<table align="center" height="10%" width="90%">
<tr>
<td>
<div id="Items">
<table style="vertical-align:top; height:200px" width="100%">
<tr style="height:20px">
<th>
#Html.DisplayName("Name")
</th>
<th>
#Html.DisplayName("Description")
</th>
<th>
#Html.DisplayName("Item Type")
</th>
</tr>
#if (Model != null)
{
foreach (var item in Model.items)
{
<tr style="height:15px">
<td>
#Html.HiddenFor(model => item.ItemId)
#Html.DisplayFor(model => item.Name)
</td>
<td>
#Html.DisplayFor(model => item.Description)
</td>
<td>
#Html.DisplayFor(model => item.Type)
</td>
<td>
#Html.ActionLink("Edit", "CreateEdit", new { id = item.ItemId }) |
#Html.ActionLink("Details", "Item", new { id = item.ItemId }) |
#Html.ActionLink("Delete", "Delete", new { id = item.ItemId })
</td>
</tr>
}
}
</table>
</div>
</td>
</tr>
</table>
<table>
<tr>
<td>
<div id="ItemDetails">
#if (Model.item != null)
{
Html.RenderPartial("Details", Model.item);
}
</div>
</td>
</tr>
</table>
<script type="text/javascript">
$(function () {
$("div.Items a").click(function (e) {
//e.preventDefault();
var url = this.ref;
$("#ItemDetails").load(url);
});
});
</script>
Controller code:
List<Item> items = new List<Item>();
// GET: Item
public ActionResult Item(int id = 0)
{
ItemViewModel itemVM = new ItemViewModel();
itemVM.items = GetItems();
if (id > 0)
itemVM.item = itemVM.items.FirstOrDefault(u => u.ItemId == id);
return View(itemVM);
}
Any clues?
First of all, you are waiting for clicks on a class called Items. This is the reason the jquery code is not being fired. You should have
$("div#Items a").click(function (e) {
Secondly, you need to check the attribut href, not ref. Also, prevent default needs to be there, otherwise you just reload your page
e.preventDefault();
var url = this.href;
You don't need the renderpartial in the page, just leave it empty:
<div id="ItemDetails">
</div>
While your logic kind of works, the way the url is loaded causes the entire page to be loaded into the ItemDetails section. I'd suggest that in your controller, you'd create a separate method for the details:
public ActionResult Details(int id) {
Item item = GetItem(id);
return View(item);
}
private Item GetItem(int id) {
return new Item() { Details = "details here" };
}

How to create multiple in-line forms for a list property

I want to create a view where a user can update (create/edit) multiple Journal Entries (one for each journal) at the same time.
ViewModel:
public class CompanyRecoMonthViewModel
{
public Company Company { get; set; }
public string RecoMonth { get; set; }
}
Company has a list of Journals:
public virtual IList<Journal> Journals { get; set; }
Every journal has a list of JournalEntries
public IList<JournalEntry> JournalEntries { get; set; }
In each month Journal will have one (or none) JournalEntry .
From the controller I'm loading the Journals into the view.
Now the code in the view. I'm trying to put the inline forms into a table view.
My problem is that upon submitting the form none of the values are captured.
Probably not using the #Html.BeginForm properly. Seems that form doesn't know which object it is meant to edit.
<table class="table">
<tr>
<th>
Person Responsible
</th>
<th>
Journal Number
</th>
<th>
SomeID
</th>
<th>
Status
</th>
<th>
Upload Date
</th>
<th>
Amount<
</th>
<th></th>
</tr>
#foreach (var item in Model.Company.Journals)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.User.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.JournalNumber)
</td>
<!-- this is where we start inline form for Journal Entry-->
#using (Html.BeginForm("Edit", "JournalEntries", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
<form class="form-inline">
#Html.HiddenFor(modelItem => item.JournalID)
<div class="form-group">
<td>
#Html.TextBoxFor(modelItem => item.JournalEntries[0].SomeID, new { #class = "form-control", maxlength = "8" })
</td>
<td>
#Html.DropDownListFor(modelItem => item.JournalEntries[0].Status, Model.JournalStatuses.Select(e => new SelectListItem { Text = e }), string.Empty, new { #class = "form-control" })
</td>
<td>
#Html.EditorFor(modelItem => item.JournalEntries[0].DatePosted, new { htmlAttributes = new { #class = "form-control datepicker" } })
</td>
<td>
#Html.DropDownListFor(modelItem => item.JournalEntries[0].JournalType, Model.JournalTypes.Select(e => new SelectListItem { Text = e }), string.Empty, new { #class = "form-control" })
</td>
<td>
#Html.EditorFor(modelItem => item.JournalEntries[0].AmountPosted, new { htmlAttributes = new { #class = "form-control" } })
</td>
<td>
<button type="submit" class="btn btn-default">Save</button>
</td>
</div>
</form>
}
</tr>
}
</table>
Add virtual keyword to JournalEntries property in your Journal class to navigate to the JournalEntry class.
public virtual IList<JournalEntry> JournalEntries { get; set; }
Managed to achieve that using multiple partial views with inline-forms for one entity per partial view.

Error: The call is ambiguous between the following methods or properties

I get this error:
The call is ambiguous between the following methods or properties:
DisplayNameFor<IEnumerable<Category>,string>(HtmlHelper<IEnumerable<Category>>, System.Linq.Expressions.Expression<System.Func<IEnumerable,string>>)
and
DisplayNameFor<Category,string>(HtmlHelper<IEnumerable>, System.Linq.Expressions.Expression<System.Func<Category,string>>)
My model is
public class Category
{
public int CategoryId { get; set; }
public string CategoryName { get; set; }
}
My Context Model is
public class CategoryContext : DbContext
{
public DbSet<Category> category { get; set; }
}
My Controller is :
public ActionResult GetCategory()
{
using (CategoryContext cc = new CategoryContext())
{
var cat = cc.category.ToList();
return View();
}
}
My View is :
#model IEnumerable<CRUD_Manav_EF.Models.Category>
<h1>Get Category</h1>
<table>
<tr>
<th>#Html.DisplayNameFor(model => model.CategoryName)</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayNameFor(modelItem => item.CategoryName) // I get error here
</td>
<td>
#Html.ActionLink("Edit", "Update", new { id = item.CategoryId })
#Html.ActionLink("Details", "Details", new { id = item.CategoryId })
#Html.ActionLink("Delete", "Delete", new { id = item.CategoryId })
</td>
</tr>
}
</table>
This error is displaying because you have used
#Html.DisplayNameFor(model => model.CategoryName) in table foreach loop and call would ambiguous between those method mentioned above. Since there is no benefit of using Display name again and again while iterating. if you will see the whole description of #Html.DisplayNameFor() you will get that the first parameter accept only model(lambda expression) and not IEnumerable of model. This is also shown in your compiler error.
see in example screenshot (this is dummy project)
Use #html.DisplayFor(..) instead in your foreach loop.
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.CategoryName)
</td>
<td>
#Html.ActionLink("Edit", "Update", new { id = item.CategoryId })
#Html.ActionLink("Details", "Details", new { id = item.CategoryId })
#Html.ActionLink("Delete", "Delete", new { id = item.CategoryId })
</td>
</tr>
}
This htmlhelper method will take IEnumerable of your model. And your issue will be resolved(you can check it yourself).

lambda expression Problems

My question is when I click actionlink,the view send specific ID to controller(ex. ProductID = 6), but my controller grab all data to me not specific ID data.
I think the problem is the lambda expression at controller, it will grab all data to me.
These are my Models:
public class ShoppingCart
{
public List<ShoppingCartItemModel> items = new List<ShoppingCartItemModel>();
public IEnumerable<ShoppingCartItemModel> Items
{
get { return items; }
}
}
public class ShoppingCartItemModel
{
public Product Product
{
get;
set;
}
public int Quantity { get; set; }
}
Controller :
[HttpGet]
public ActionResult EditFromCart(int ProductID)
{
ShoppingCart cart = GetCart();
cart.items.Where(r => r.Product.ProductID == ProductID)
.Select(r => new ShoppingCartItemModel
{
Product = r.Product,
Quantity = r.Quantity
});
return View(cart);
//return RedirectToAction("Index", "ShoppingCart");
}
private ShoppingCart GetCart()
{
ShoppingCart cart = (ShoppingCart)Session["Cart"];
//如果現有購物車中已經沒有任何內容
if (cart == null)
{
//產生新購物車物件
cart = new ShoppingCart();
//用session保存此購物車物件
Session["Cart"] = cart;
}
//如果現有購物車中已經有內容,就傳回 view 顯示
return cart;
}
View
#model ShoppingCart
#{
ViewBag.Title = "購物車內容";
}
<h2>Index</h2>
<table class="table">
<thead>
<tr>
<th>
Quantity
</th>
<th>
Item
</th>
<th class="text-right">
Price
</th>
<th class="text-right">
Subtotal
</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model.items)
{
<tr>
<td class="text-center">
#item.Quantity
</td>
<td class="text-center">
#item.Product.ProductName
</td>
<td class="text-center">
#item.Product.Price.ToString("c")
</td>
<td class="text-center">
#( (item.Quantity * item.Product.Price).ToString("c"))
</td>
<td>
#using (Html.BeginForm("RemoveFromCart", "ShoppingCart"))
{
#Html.Hidden("ProductId", item.Product.ProductID)
#*#Html.HiddenFor(x => x.ReturnUrl)*#
<input class="btn btn-warning" type="submit" value="Remove">
}
</td>
<td>
#using (Html.BeginForm("EditFromCart", "ShoppingCart", FormMethod.Get))
{
#Html.Hidden("ProductId", item.Product.ProductID)
<input class="btn btn-warning" type="submit" value="Edit">
}
</td>
</tr>
}
</tbody>
</table>
The key issue is that this code doesn't return the result of the LINQ queries, because you have not assigned a variable to the result:
cart.items.Where(r => r.Product.ProductID == ProductID)
.Select(r => new ShoppingCartItemModel
{
Product = r.Product,
Quantity = r.Quantity
});
I strongly suggest you create a viewmodel specifically to display the cart items.
public class CartItemsViewModel
{
public List<ShoppingCartItemModel> Items { get; set; }
}
[HttpGet]
public ActionResult EditFromCart(int ProductID)
{
ShoppingCart cart = GetCart();
var viewModel = new CartItemsViewModel();
viewModel.Items.AddRange(cart.items.Where(r => r.Product.ProductID == ProductID)
.Select(r => new ShoppingCartItemModel
{
Product = r.Product,
Quantity = r.Quantity
}));
return View(viewModel);
}
In my example I use the .AddRange() method to take the results of the LINQ calls against the cart items and store them in the viewmodel's Items property.
You must have to assign filtered value to cart like this.
cart.item = cart.items.Where(r => r.Product.ProductID == ProductID)
.Select(r => new ShoppingCartItemModel
{
Product = r.Product,
Quantity = r.Quantity
}).ToList();
use ToList(); or FirstOrDefault() as per your condition
You need to hold the return value from the linq query on cart.Items in a variable and pass that to the View method.
At the moment, the result of your query is being lost and the whole cart passed to the View method.

Resources