Postback model is null - asp.net-mvc

I changed my index view so that each row is wrapped in a form:
Index.cshtml
#model IEnumerable<LevEl.Models.Page>
#foreach (var item in Model)
{
using (Html.BeginForm("Edit", "Pages", item, FormMethod.Post, new { id = "itemForm_" + item.PageId }))
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Title)
</td>
...
... more fields
...
<td>
#Html.CheckBoxFor(modelItem => item.IsPublished, new { onClick = "$(itemForm_" + item.PageId + ").submit();" })
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id = item.PageId }) | #Html.ActionLink("Details", "Details", new { id = item.PageId }) | #Html.ActionLink("Delete", "Delete", new { id = item.PageId })
</td>
</tr>
}
}
Here's my controller:
PagesController.cs
public ActionResult Index()
{
return View(db.Pages.Include(p => p.Language).ToList());
}
[HttpPost]
public ActionResult Edit(Page page)
{
var method = Request.HttpMethod;
if (ModelState.IsValid)
{
if (page.PageContents.GroupBy(pc => pc.Language).Any(g => g.Count() > 1))
return HttpNotFound("A page cannot have two translations in the same language.");
db.Entry(page).State = EntityState.Modified;
db.SaveChanges();
ClearMenuKeys();
return RedirectToAction("Index");
}
return View(page);
}
When I switch the value in the check box and the controller is reached, page is null.
How can I pass the entire page object upon postback?

It seems to me like your HTML do not store values of the Page. #Html.DisplayFor only displays data you have to set #Html.HiddenFor for that field too.
EDIT:
#Html.DisplayFor(modelItem => item.Title)
#Html.HiddenFor(modelItem => item.Title)

Related

.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" };
}

Model is not populated during form post 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.

How to do a partial view on ActionLink

<table>
<tr>
<th>
Author
</th>
<th>
Title
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Author)
</td>
<td>
#Html.DisplayFor(modelItem => item.Title)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.ID }) |
#Html.ActionLink("Details", "Details", new { id=item.ID }) |
#Html.ActionLink("Delete", "Delete", new { id=item.ID })
#Ajax.ActionLink("View Reviews", "View", new { id = item.ID }, new AjaxOptions { UpdateTargetId = "result", InsertionMode = InsertionMode.InsertAfter })
</td>
</tr>
}
</table>
<div id="result">
</div>
public PartialViewResult View(int id)
{
ReviewModel reviewModel = new ReviewModel();
return PartialView(reviewModel.GetReviews(id));
}
Once the response has been sent to the client, the server is done. That means you can't directly have a partial view rendered based on a click, because those are two disjointed processes: a partial view is rendered server-side, while a click is registered client-side, once the server is already out of the picture.
Anytime you're talking about changing the already rendered page in client with some new information from the server, you're talking about AJAX, so you will need to handle that with JavaScript on the client-side, that catches the click event and requests the partial view from the server. That means you'll also need an action, server-side that your JavaScript can send a request to and that returns your partial view.
ReviewsController
public ActionResult View(int id)
{
// fetch reviews for `id`
if (Request.IsAjaxRequest())
{
// return partial for AJAX requests
return PartialView("_ReviewsPartial", reviews);
}
else
{
// return full view for regular requests
return View(reviews);
}
}
Main View
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Author)
</td>
<td>
#Html.DisplayFor(modelItem => item.Title)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.ID }) |
#Html.ActionLink("Details", "Details", new { id=item.ID }) |
#Html.ActionLink("Delete", "Delete", new { id=item.ID })
#Html.ActionLink("View Reviews", "View", new { id=item.ID }, new { #class = "GetReviewsLink", data_id = item.ID })
</td>
</tr>
}
<div id="SomeDiv"></div>
JavaScript
$(document).ready(function () {
$('.GetReviewsLink').on('click', function (e) {
e.preventDefault();
var id = $(this).data('id');
$.get('#Url.Action("View")', { id: id }, function (result) {
$('#SomeDiv').html(result);
});
});
});
That will dump the rendered partial from the server into the div with id, "SomeDiv". You can then display it however you want. For example, you may have a static region that will just switch out the reviews as each item's link is clicked. "SomeDiv" may actually be the inner part of a modal window, that you could then show after the new content has been loaded in. However you want to handle the display.

How to highlight a new added entry in MVC?

I've created a DB application in MVC 4 using EntityFramework. It works okay, however I couldn't find the way of highlighting new added item to my table(html). Is it possible to highlight new rows in DB applications in MVC? Like in C#:
DataGrid.SelectedItem
Any advice or help would be very clarifying. How to highlight a row in MVC?
How are you rendering the table?
One option is to use TempData. Store the identifier of the added item in TempData and check for the identifier when rendering the table.
ItemController:
// POST: /Item/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include="Id,Name")] Item item)
{
if (ModelState.IsValid)
{
db.Items.Add(item);
db.SaveChanges();
TempData["AddedItemId"] = item.Id;
return RedirectToAction("Index");
}
return View(item);
}
Item view:
<!-- Views\Item\Index.cshtml -->
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Name)
</th>
<th></th>
</tr>
#foreach (var item in Model)
{
var className = "";
if (TempData["AddedItemId"] != null && (int)TempData["AddedItemId"] == item.Id)
{
className = "alert-info";
}
<tr class="#className">
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id = item.Id }) |
#Html.ActionLink("Details", "Details", new { id = item.Id }) |
#Html.ActionLink("Delete", "Delete", new { id = item.Id })
</td>
</tr>
}
</table>

MVC NullReferenceException

When I am sending information to the Database in MVC and I am redirecting the page and calling the view which prints out the newly stored data, I get a NullReferenceException error:
"Object reference not set to an instance of an object."
Apparently, my Model is null:
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.CustomerID)
</td>
<td>
#Html.DisplayFor(modelItem => item.Address)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { /* id=item.PrimaryKey */ }) |
#Html.ActionLink("Details", "Details", new { /* id=item.PrimaryKey */ }) |
#Html.ActionLink("Delete", "Delete", new { /* id=item.PrimaryKey */ })
</td>
</tr>
}
This is my Controller:
public ActionResult Index()
{
var customer = db.Customer.ToList();
return View();
}
Which gets called by:
[HttpPost]
public ActionResult Create(CustomerModel customer)
{
db.Customer.Add(customer);
db.SaveChanges();
return RedirectToAction("Index");
}
I don't understand why it's null....
public ActionResult Index()
{
var customer = db.Customer.ToList();
return View(customer);
}
You are never passing the customer object to the view.
Edit: Also not wrapping your db.SaveChanges(); in a try/catch can be dangerous when an exception occurs.

Resources