I am using the following code:
Controller:
public ActionResult Update(int studentId = 0, int subjectId = 0)
{
Engine engine = new Engine(studentId, subjectId);
List<Chapter> chapterList = engine.GetChapters();
return View(chapterList);
}
[HttpPost]
public ActionResult Update(List<Chapter> model)
{
return View(model);
}
Update.cshtml:
#model IEnumerable<Chapter>
#{
ViewBag.Title = "Update";
}
<h2>
Update</h2>
#using (Html.BeginForm("Update", "StudyPlan", FormMethod.Post))
{
<fieldset>
<table>
#foreach (var item in Model)
{
<tr>
<td>
#item.name
</td>
<td>
#Html.CheckBoxFor(chapterItem => item.included)
</td>
</tr>
}
</table>
<input type="submit" value="submit" />
</fieldset>
}
I want when a user selects checkboxes, the response should come in httppost method of controller. But I am getting null value Update method. Am I doing something wrong
You need to use for instead of foreach. In that case checkbox will be rendered as
<input type='checkbox' name='Model[0].included'/>
<input type='checkbox' name='Model[1].included'/>
...
and then ModelBinder will successfully create model
Example:
#model List<Chapter>
#{
ViewBag.Title = "Update";
}
<h2>
Update</h2>
#using (Html.BeginForm("Update", "StudyPlan", FormMethod.Post))
{
<fieldset>
<table>
#for (int i =0;i<Model.Count;i++)
{
<tr>
<td>
#Model[i].name
</td>
<td>
#Html.CheckBoxFor(chapterItem => Model[i].included)
</td>
</tr>
}
</table>
<input type="submit" value="submit" />
</fieldset>
}
PS. in that example Model changed to List<> from IEnumerable
It happens because MVC analyze expression in CheckBoxFor method. And it this expression is array accessor, then it generates different control name. And based on Name ModelBinder successfully creates List<>
As Sergey suggested, use a for loop, but try this:
#for (int i =0;i<Model.Count;i++)
{
<tr>
<td>
#Html.HiddenFor(m => m[i].id)
#Html.DisplayFor(m => m[i].name)
</td>
<td>
#Html.CheckBoxFor(m => m[i].included)
</td>
</tr>
}
Related
I have a problem when I am trying to post IEnumerable from razor view to Controllor action method. Also result is the same if I use List.
I post my controllor action method also in comment. In my controllor action method I got list that is empty.
This is my View:
#model IEnumerable<Subject>
<form asp-action="AddNewSubjects" asp-controller="Teacher" method="post" role="form" class="form-horizontal">
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Number of class</th>
<th>Level</th>
</tr>
</thead>
<tbody>
#if (Model != null)
{
var item = Model.ToList();
#for(int i=0;i<Model.Count();i++)
{
<tr>
<td>#item[i].ID</td>
<td>#item[i].Name</td>
<td>#item[i].ClassNumber</td>
<td>#item[i].Level</td>
</tr>
}
}
</tbody>
</table>
<div class="form-group">
<div class="col-md-offset-2 col-md-5">
<input type="submit" class="btn btn-primary" value="Save all subjects" />
</div>
</div>
</form>
This is my Controller:
private readonly ISubjectService _subjectService;
public TeacherController(ISubjectService subjectService)
{
_subjectService= subjectService;
}
[HttpPost]
public IActionResult AddNewSubjects(IEnumerable<Subject> subjects)
{
var newSubjects= (from p in subjects
where p.State== Status.New
select p);
var result = _subjectService.SaveTeacherSubjects(newSubjects);
return View("ProfesorPages");
}
I have no idea what you're trying to do here. Your form doesn't have any input element except the submit button. Of course you're not seeing anything posted back.
#model IEnumerable<Subject>
<form>
...
<tbody>
#for(int i = 0; i < Model.Count(); i++)
{
<tr>
<td>
<input type="hidden" asp-for="Model[i].ID" />
</td>
<td>
<input type="text" asp-for="Model[i].Name" />
</td>
...
</tr>
}
</tbody>
...
</form>
Why??
Why did you convert your IEnumerable to a list named item? Why not just enumerate your subjects directly?
Why not create a different set of models called ViewModel and pass that to the View, instead of using your model from your database directly on the View?
Context
I'm developing a form in ASP.NET MVC 4 with Bootstrap 3 which displays an entity framework derived table. The table is read-only except that there is a button in the first column of each row allowing that row to be deleted.
My ViewModel:
public class SearchQueueViewModel
{
public List<SearchURL> SearchUrls { get; set; }
[Url(ErrorMessage = "Please provide a valid url")]
[MaxLength(500, ErrorMessage = "Url must be 500 characters or less")]
[Required]
public string NewUrl { get; set; }
}
The table is coded like this:
#model SearchQueueViewModel
#foreach (UserInterface.Models.SearchURL row in Model.SearchUrls)
{
<tr>
<td class="col-md-1 text-center">
#using (Html.BeginForm("delete", "home", FormMethod.Post, new { #class = "form-inline" }))
{
#Html.AntiForgeryToken()
<input type="hidden" value="#row" name="searchURL" />
<button type="submit" class="btn btn-xs">X</button>
}
</td>
<td class="col-md-2">#Html.DisplayFor(m => row.URL)</td>
<td class="col-md-2">#row.DateAdded.ToString("d-MMM-yy HH:mm")</td>
<td class="col-md-1">#Html.DisplayFor(m => row.Status)</td>
<td class="col-md-1">#Html.DisplayFor(m => row.SearchStarted)</td>
<td class="col-md-1">#Html.DisplayFor(m => row.SearchCompleted)</td>
<td class="col-md-1">#Html.DisplayFor(m => row.NumberOfProductsSearched)</td>
<td class="col-md-1">#Html.DisplayFor(m => row.NumberOfProductsFound)</td>
<td class="col-md-2">#Html.DisplayFor(m => row.FailErrorMessage)</td>
</tr>
}
In the controller I have:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Delete(SearchURL searchURL)
{
context.SearchURLs.Remove(searchURL);
context.SaveChanges();
viewModel.SearchUrls = context.SearchURLs.ToList();
return View(viewModel);
}
Issue
The controller always receives a null object
Question
How do I get the searchURL object from the button-clicked row passed back to the controller correctly?
Note the for loop insted of foreach to enable model binding and the hidden fields to allow the values to be posted back to the controller. Change your view.
Your View:
#for (int i = 0; i < Model.SearchUrls.Count; i++)
{
<tr>
<td class="col-md-1 text-center">
#using (Html.BeginForm("delete", "home", FormMethod.Post, new { #class = "form-inline" }))
{
#Html.AntiForgeryToken()
#Html.HiddenFor(m=>Model.SearchUrls[i].SearchURLId)
<button type="submit" class="btn btn-xs">delete</button>
}
</td>
<td class="col-md-2">#Html.DisplayFor(m => Model.SearchUrls[i].SearchURLId.URL)</td>
<td class="col-md-2">#Model.SearchUrls[i].DateAdded.ToString("d-MMM-yy HH:mm")</td>
<td class="col-md-1">#Html.DisplayFor(m => Model.SearchUrls[i].Status)</td>
<td class="col-md-1">#Html.DisplayFor(m => Model.SearchUrls[i].SearchStarted)</td>
<td class="col-md-1">#Html.DisplayFor(m => Model.SearchUrls[i].SearchCompleted)</td>
<td class="col-md-1">#Html.DisplayFor(m => Model.SearchUrls[i].NumberOfProductsSearched)</td>
<td class="col-md-1">#Html.DisplayFor(m => Model.SearchUrls[i].NumberOfProductsFound)</td>
<td class="col-md-2">#Html.DisplayFor(m => Model.SearchUrls[i].FailErrorMessage)</td>
</tr>
}
And also change your Delete method.
public ActionResult delete(SearchQueueViewModel sm) // note this line
{
//your code
}
But the job is not finished yet. If I am not wrong now you are going to face issue with multiple submit button with identical property.
The best approach for your requirement is using javascript. You can have a look to this
So my Index view renders the view called StudentWellnessReviews retrieves reviews from the dbo.Review Table. Everything was working until I tried to setup pagination for the StudentWellnessReviews view in the ReviewsController. The error was:
Only one 'model' statement is allowed in a file.
ReviewsController
public class ReviewsController : Controller
{
private SizaData_1Entities db = new SizaData_1Entities();
// GET: Reviews
public ActionResult Index()
{
return View();
}
//Student Wellness Reviews
public ActionResult StudentWellnessReviews(int page = 1, int pageSize = 4)
{
using (var context = new SizaData_1Entities())
{
List<Review> listReviews = context.Reviews.SqlQuery("select * from dbo.Review where WellnessService='Student Wellness Service'").ToList();
PagedList<Review> userreview = new PagedList<Review>(listReviews, page, pageSize);
return View(userreview);
}
}
StudentWellnessReviews View:
#model IEnumerable<Siza.Models.Review>
#{
ViewBag.Title = "Index";
Layout = "";
}
#model PagedList.IPagedList<Siza.Models.Review>
#using PagedList.Mvc;
<table class="table text-center width:50%">
#foreach (var item in Model)
{
<tr>
<td>
<h5>Username</h5>
</td>
<td>
<p>
<div align="left">#Html.DisplayFor(modelItem => item.Username)</div>
</p>
</td>
</tr>
<tr>
<td>
<h5>Wellness Service</h5>
</td>
<td>
<p>
<div align="left">#Html.DisplayFor(modelItem => item.WellnessService)</div>
</p>
</td>
</tr>
<tr>
<td>
<h5>Rating</h5>
</td>
<td>
<p>
<div align="left">#Html.DisplayFor(modelItem => item.Rating)</div>
</p>
</td>
</tr>
<tr>
<td>
<h5>Feedback</h5>
</td>
<td>
<p>
<div align="left"> #Html.TextAreaFor(modelItem => item.Feedback, new {#readonly = true})</div>
</p>
</td>
</tr>
<tr>
<td>
<h5>Date Created</h5>
</td>
<td>
<p>
<div align="left"> #Html.DisplayFor(modelItem => item.Date)</div>
</p>
</td>
</tr>
<tr><td colspan="2"><hr class="active"/></td></tr>
}
#Html.PagedListPager(Model, Page => Url.Action("StudentWellnessReviews",
new { Page, pageSize = Model.PageSize} ))
Showing #Model.FirstItemOnPage to #Model.LastItemOnPage of #Model.TotalItemCount Reviews
</table>
Part of Index view
#using Siza.Controllers
#{
ViewBag.Title = "Index";
}
#{Html.RenderAction("StudentWellnessReviews", "Reviews");}
Assistance would be greatly appreciated.
The problem is you cannot define model for your View multiple times, right n ow you are saying that your view is strongly typed with both IEnumerable<Siza.Models.Review> and PagedList.IPagedList<Siza.Models.Review> which cannot be, so what i see is you just need PagedList.IPagedList<Siza.Models.Review> so remove the top line where you are setting model to IEnumerable<Siza.Models.Review> to be like:
#model PagedList.IPagedList<Siza.Models.Review>
#using PagedList.Mvc;
#{
ViewBag.Title = "Index";
Layout = "";
}
.................
Your rest view
I am trying to pass values from a view to a controller in MVC. I am using a ViewModel and normally the values would bind to the properties as long as the names are the same. However because the values are generated via a foreach loop the names of the values do not match the names of the properties in the view model.
I am working around this by assigning the values to a variable in Razor. However one of my values is in a text box on the form and the value is not being passed to the controller and I cannot work out why.
I get a null exception when clicking the button.
VIEW Code is below:
#model PagedList.IPagedList<Mojito.Domain.ViewModels.ShoppingCartProductItem>
#using System.Web.UI.WebControls
#using PagedList.Mvc;
<link href="~/Content/PagedList.css" rel="stylesheet" type="text/css" />
#{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Mojito Products</h2>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.FirstOrDefault().Description)
</th>
<th>
#Html.ActionLink("Price", "Index", new { sortOrder = ViewBag.SortByPrice, currentFilter = ViewBag.CurrentFilter })
</th>
<th>
#Html.DisplayNameFor(model => model.FirstOrDefault().Quantity)
</th>
<th>
</th>
<th></th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Description)
</td>
<td>
#Html.DisplayFor(modelItem => item.Price)
</td>
<td>
#Html.TextBoxFor(modelItem => item.Quantity)
</td>
<td>
#{string Description = item.Description;}
#{decimal Price = item.Price;}
#{int Quantity = item.Quantity; }
#using (Html.BeginForm("AddToCart", "ShoppingCart", FormMethod.Post))
{
<div class="pull-right">
#if (Request.Url != null)
{
<input type="text" hidden="true" name="Description" value=#Description />
<input type="text" hidden="true" name="Price" value=#Price />
<input type="text" hidden="true" name="Quantity" value=#Quantity />
#Html.Hidden("returnUrl", Request.Url.PathAndQuery)
<input type="submit" class="btn btn-success" value="Add to cart" />
}
</div>
}
</td>
</tr>
}
</table>
<div class="col-md-12">
Page #(Model.PageCount < Model.PageNumber ? 0 : Model.PageNumber) of #Model.PageCount
</div>
#Html.PagedListPager(Model, page => Url.Action("Index",
new { page, sortOrder = ViewBag.CurrentSort, currentFilter = ViewBag.CurrentFilter }))
Controller Code below
public ActionResult AddToCart(Cart cart, MojitoProduct product, string returnUrl, int Quantity =1)
{
if (product != null)
{
cart.AddItem(product, Quantity);
}
return RedirectToAction("Index", new { returnUrl });
}
Do not use foreach. Use a for-loop instead and within this, qualify the full path to your properties using the index.
Better yet: use a Edit- or DisplayTemplate for the ShoppingCartProductItem. This will also keep your path.
You have to use for loop instead of foreach:
#for (int i=0;i < Model.Count; i++)
{
<tr>
<td>
#Html.DisplayFor(modelItem => Model[i].Description)
</td>
<td>
#Html.DisplayFor(modelItem => Model[i].Price)
</td>
<td>
#Html.TextBoxFor(modelItem => Model[i].Quantity)
</td>
..........................
..........................
..........................
}
you can also post all using one form by posting List<ShoppingCartProductItem>, see Model Binding To A List
Your textboxes so values out of the form.
Try like below
#using (Html.BeginForm("AddToCart", "ShoppingCart", FormMethod.Post))
{
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Description)
</td>
<td>
#Html.DisplayFor(modelItem => item.Price)
</td>
<td>
#Html.TextBoxFor(modelItem => item.Quantity)
</td>
<td>
#{string Description = item.Description;}
#{decimal Price = item.Price;}
#{int Quantity = item.Quantity; }
<div class="pull-right">
#if (Request.Url != null)
{
<input type="text" hidden="true" name="Description" value=#Description />
<input type="text" hidden="true" name="Price" value=#Price />
<input type="text" hidden="true" name="Quantity" value=#Quantity />
#Html.Hidden("returnUrl", Request.Url.PathAndQuery)
<input type="submit" class="btn btn-success" value="Add to cart" />
}
</div>
</td>
</tr>
}
}
I resolved this in the short term by using new and forcing the name of the parameter.
#Html.HiddenFor(modelItem => t.NoOfUsers, new { Name = "NoOfUsers", id = "NoOfUsers" })
how can i get the querystring id in there? is there any way
#using (Html.BeginForm("InformVendor", "Procurement",new {id="querystring Mode = "Inform" }, FormMethod.Post))
{
<tr>
<td>
#Html.DropDownListFor(m=>m.VendorId,new MultiSelectList(Model.VendorDropdownlist, "CustomerId", "ContactName"))
</td>
<td>
#Html.CheckBoxFor(m => m.IsEmail)
</td>
</tr>
<tr>
<td>
<input type="submit" id="btnsubmit" value="Nominate Vendor" />
</td>
<td></td>
</tr>
}
The easiest way is to have your id field be hidden. This way the user doesn't see the ID field but your post back controller does.
#Html.HiddenFor(x => x.YourID)
If you add the Id to your view model and render it as a hidden field.
#Html.HiddenFor(m => m.Id)
You will be able to retrieve it like this instead of using the querystring.
public ActionResult InformVendor(AViewModel model)
{
var Id = model.Id;
}