Bind Textbox to model in MVC4 - c#

In my MVC application, I have specified two conditions based on the Model.Count to display the values in View.
View
#model IEnumerable<SampleECommerce.Models.DetailsModel>
#using (Html.BeginForm("Details", "Grid", new { UserID = Request.QueryString["UserID"], partnerid = Request.QueryString["Partnerid"] }, FormMethod.Post))
{
if (Model.Count() == 0)
{
#foreach (var item in Model)
{
<table>
<tr>
<td>
#Html.DisplayNameFor(model => model.FirstName)
<input id="FirstName" type="text" class="TextBoxBorder" name="FirstName" value="#item.FirstName" /> // When the Model count is zero, the label and textbox is not displayed.
</td>
</tr>
</table>
}
else
{
#foreach (var item in Model)
{
<table>
<tr>
<td>
#Html.DisplayNameFor(model => model.FirstName)
<input id="MFirstName" type="text" class="TextBoxBorder" name="FirstName" value="#item.FirstName" />
</td>
</tr>
</table>
}
Controller
public ActionResult Details()
{
string userid = Request.QueryString["UserID"];
string partnerid = Request.QueryString["Partnerid"];
con.Open();
SqlCommand cmd = new SqlCommand("select FirstName from Details where UserID = +userid+", con);
SqlDataReader dr = cmd.ExecuteReader();
List<DetailsModel> objmodel = new List<DetailsModel>();
while (dr.Read())
{
objmodel.Add(new DetailsModel()
{
FirstName = dr["First Name"].ToString(),
});
}
dr.Close();
return View(objmodel);
}
When the Model.Count is zero, the label and textbox are not displayed.
I am trying to insert new value to textbox when the model.count is zero based on the userid
I tried to bind the textbox to model all the ways specified in the Link.
1. #Html.TextBoxFor(model => model.FirstName)
Error in FirstName stating "System.Collections.Generic.IEnumerable doesnot find definition for FirstName or no extension method "
2. #Html.TextBox(model=>model.FirstName)
"Error stating Cannot convert Lamba expression to string type"
How to bind and display the textbox value to the model when the model.count is zero.
Any suggestions ??

when the Model.Count is 0, foreach does nothing .
#model IEnumerable<SampleECommerce.Models.DetailsModel>
#using (Html.BeginForm("Details", "Grid", new { UserID = Request.QueryString["UserID"], partnerid = Request.QueryString["Partnerid"] }, FormMethod.Post))
{
<table>
if (Model.Count() == 0)
{
<tr>
<td>
#Html.DisplayNameFor(model => model.FirstName)
<input id="FirstName" type="text" class="TextBoxBorder" name="FirstName" /> // When the Model count is zero, the label and textbox is not displayed.
</td>
</tr>
}
else
{
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayNameFor(model => model.FirstName)
<input id="MFirstName" type="text" class="TextBoxBorder" name="FirstName" value="#item.FirstName" />
</td>
</tr>
}
}
<tr>
<td>
<input type="submit" value="submit" />
</td>
</tr>
</table>
}

Related

How to get an Id from one table and save it to another table in ASP.NET Core, repository pattern

This is Schedule Exam service:
public int AddSchedule(ScheduleExamViewModel schedule)
{
var newSchedule = new ScheduleExam()
{
ExamDate = schedule.ExamDate,
SubjectId = schedule.SubjectId,
StudentId = schedule.StudentId,
Status = schedule.Status
};
_context.ScheduleExams.Add(newSchedule);
_context.SaveChanges();
return newSchedule.Id;
}
This is Schedule exam controller :
// GET: ScheduleExamController/Create
public IActionResult Create()
{
var model = new ScheduleExamViewModel();
ViewBag.Subject = new SelectList(_isubject.GetSubject(), "Id", "Name");
ViewBag.Student = new SelectList(_istudent.GetStudent(), "Id", "Name");
return View(model);
}
// POST: ScheduleExamController/Create
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Create(ScheduleExamViewModel schedule)
{
if (ModelState.IsValid)
{
int id = _ischeduleExam.AddSchedule(schedule);
if (id > 0)
{
return RedirectToAction(nameof(Create));
}
}
ViewBag.Subject = new SelectList(_isubject.GetSubject(), "Id", "Name");
ViewBag.Student = new SelectList(_istudent.GetStudent(), "Id", "Name");
return View(schedule);
}
I want to get the Id of student and save it to table of schedule exam by clicking on the "Schedule New Exam"
This is the index.cshtml of the Student table:
<table class="table table-hover table-bordered table-condensed">
<thead class="table-color text-white">
<tr>
<th>
#Html.DisplayNameFor(model => model.Id)
</th>
<th>
#Html.DisplayNameFor(model => model.Name)
</th>
<th>
#Html.DisplayNameFor(model => model.LastName)
</th>
<th>
#Html.DisplayNameFor(model => model.Email)
</th>
<th>Schedule New Exam</th>
<th>Properties</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Id)
</td>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.LastName)
</td>
<td>
#Html.DisplayFor(modelItem => item.Email)
</td>
<td>
<a asp-action="Create" asp-controller="ScheduleExam", asp-route-id="#item.Id">Schedule New Exam</a>
</td>
<td>
#Html.ActionLink(" ", "Edit", new { id = item.Id }, new { #class = "fa fa-edit", title = "Edit" }) |
#Html.ActionLink(" ", "Details", new { id = item.Id }, new { #class = "fa fa-info-circle", title = "More details" }) |
#Html.ActionLink(" ", "Delete", new { id = item.Id }, new { #class = "fa fa-trash", title = "Delete" })
</td>
</tr>
}
</tbody>
</table>
and this is the create.csthml page of schedule exam:
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="ExamDate" class="control-label"></label>
<input asp-for="ExamDate" class="form-control" />
<span asp-validation-for="ExamDate" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="StudentId" class="control-label"></label>
<input asp-for="StudentId" class="form-control" value="#ViewBag.model.Id" />
<span asp-validation-for="StudentId" class="text-danger"></span>
</div>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><strong>Subject:</strong></span>
</div><br />
<select asp-for="SubjectId" class="form-control input-hover" asp-items="ViewBag.Subject">
<option value="">Please choose a subject...</option>
</select>
<span asp-validation-for="SubjectId " class="text-danger"></span>
</div><br />
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
but when I click on the Schedule New exam in index.cshtml of Student table I get this error:
RuntimeBinderException: Cannot perform runtime binding on a null reference
CallSite.Target(Closure , CallSite , object )
CallSite.Target(Closure , CallSite , object )
System.Dynamic.UpdateDelegates.UpdateAndExecute1<T0, TRet>(CallSite site, T0 arg0)
AspNetCore.Views_ScheduleExam_Create.b__22_0() in Create.cshtml
+
<input asp-for="StudentId" class="form-control" value="#ViewBag.model.Id" />
Please solve it by details and I'm using ASP.NET Core 3.1 MVC using a repository pattern.
Thank you
first of all, you should get an Id inside Create-Action and set for students :
public IActionResult Create(int Id)
{
var model = new ScheduleExamViewModel();
ViewBag.Subject = new SelectList(_isubject.GetSubject(), "Id", "Name");
ViewBag.Student = new SelectList(_istudent.GetStudent(), "Id", "Name" , Id);
return View(model);
}
Second, I can't find a drop-down list for Students in your view. I think you should set Id in the controller for ViewModel.
var model = new ScheduleExamViewModel();
mode.Id = Id;
Third, you passed a model to the View and you can get information from the Model.
#model ScheduleExamViewModel
<input asp-for="StudentId" class="form-control" value="#Model.Id" />

How to pass multiple checkbox parameters from foreach

I have a form in my View with a table and on each cell i have a checkbox. I already have individual Ids for every single checkbox but I dont know how to pass them individually to controller action. I know how to pass single parameters over the "name" attribute but Im not sure on how to handle it with so many diffrent checkboxes.
View
#{
bool IsOwnRegistration = false;
foreach (var item in Model.Events.Where(i => i.UserId == Model.UserID && Convert.ToDateTime(i.Date) > dateTime))
{
if (item.HasCreatedOtherUsers == null)
{
IsOwnRegistration = true;
}
string Surname = "";
string Lastname = "";
<tr>
#{
foreach (var Useritem in Model.Users.Where(i => i.UserId == item.HasCreatedOtherUsers))
{
Surname = Useritem.Vorname;
Lastname = Useritem.Nachname;
}
if (IsOwnRegistration == true)
{
<th style="background-color:grey; width:33%;">
Meine Reservation
</th>
<th style="width:33%;">#item.Date</th>
<th style="width:33%;">
<div class="custom-control custom-checkbox ">
<input type="checkbox" class="custom-control-input" name="#item.EventId" id="#item.Date#item.EventId">
<label class="custom-control-label" for="#item.Date#item.EventId"><i style="color:red;" class="fas fa-trash-alt"></i>
</label>
</div>
</th>
}
else
{
<th style="width:33%;">#Surname #Lastname</th>
<th style="width:33%;">#item.Date</th>
<th style="width:33%;">
<div class="custom-control custom-checkbox ">
<input type="checkbox" class="custom-control-input" name="#item.EventId" id="#item.Date#item.EventId">
<label class="custom-control-label" for="#item.Date#item.EventId"><i style="color:red;" class="fas fa-trash-alt"></i>
</label>
</div>
</th>
}
}
Controller
public ActionResult DeleteRegistrations(Need to get values of all checkboxes)
{
return RedirectToAction("HomePage");
}
In order to transmit the selected checkboxes to the action method, the checkboxes should share a common name that matches the parameter name of the action method. The following sample shows a Razor view that lists some strings in a table, creating a checkbox for each string:
#model IEnumerable<string>
#{
ViewBag.Title = "Home Page";
}
<div class="row">
<div class="col-md-12">
#using (Html.BeginForm("Index", "Home", FormMethod.Post))
{
<table>
#foreach (var s in Model)
{
<tr>
<td>
<input type="checkbox" name="selectedValues" value="#s" id="#s" />
<label for="#s">#s</label>
</td>
</tr>
}
</table>
<input type="submit" />
}
</div>
</div>
Please note that the name attribute of the checkboxes is set to "selectedValues", the value attribute is set to the original string (or an id in a more complex scenario).
The form is submitted by a POST request to the following action method:
[HttpPost]
public ActionResult Index(IEnumerable<string> selectedValues)
{
return View(selectedValues);
}
In the POST request, all the values of all checked checkboxes are transmitted as key value pairs in the form "name=value". As all of the checkboxes share the same name, ASP.NET MVC can deserialize this into an IEnumerable<string>.

Model Collections Lost When Form Submitted

Using the post request below the model returns null for both the collections yet it correctly returns the boolean attribute. My expectation was that the collections loaded into the model during the get request would persist to the post request. What am I missing?
EDIT: Essentially I am trying to update the list of invoices based on the users selection of a selectlist and a checkbox.
Controller:
[HttpGet]
[AllowAnonymous]
public async Task<ActionResult> Index(bool displayFalse = true)
{
InvoiceViewModel invoiceView = new InvoiceViewModel();
var companies = new SelectList(await DbContext.Company.ToListAsync(), "CompanyID", "Name").ToList();
var invoices = await DbContext.Invoice.Where(s => s.Paid.Equals(displayFalse)).ToListAsync();
return View(new InvoiceViewModel { Companies = companies,Invoices = invoices, SelectedCompanyID = 0, DisplayPaid = displayFalse});
}
[HttpPost]
[AllowAnonymous]
public async Task<IActionResult> Index(InvoiceViewModel model)
{
model.Invoices = await DbContext.Invoice.Where(s => s.CompanyID.Equals(model.SelectedCompanyID) && s.Paid.Equals(model.DisplayPaid)).ToListAsync();
return View(model);
}
Model:
public class InvoiceViewModel
{
public int SelectedCompanyID { get; set; }
public bool DisplayPaid { get; set; }
public ICollection<SelectListItem> Companies { get; set; }
public ICollection<Invoice> Invoices{ get; set; }
}
View:
#model InvoiceIT.Models.InvoiceViewModel
<form asp-controller="Billing" asp-action="Index" method="post" class="form-horizontal" role="form">
<label for="companyFilter">Filter Company</label>
<select asp-for="SelectedCompanyID" asp-items="Model.Companies" name="companyFilter" class="form-control"></select>
<div class="checkbox">
<label>
<input type="checkbox" asp-for="DisplayPaid" />Display Paid
<input type="submit" value="Filter" class="btn btn-default" />
</label>
</div>
<br />
</form>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Invoices.FirstOrDefault().InvoiceID)
</th>
<th>
#Html.DisplayNameFor(model => model.Invoices.FirstOrDefault().CompanyID)
</th>
<th>
#Html.DisplayNameFor(model => model.Invoices.FirstOrDefault().Description)
</th>
<th>
#Html.DisplayNameFor(model => model.Invoices.FirstOrDefault().InvoiceDate)
</th>
<th>
#Html.DisplayNameFor(model => model.Invoices.FirstOrDefault().DueDate)
</th>
<th>
#Html.DisplayNameFor(model => model.Invoices.FirstOrDefault().Paid)
</th>
<th></th>
</tr>
#foreach (var item in Model.Invoices)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.InvoiceID)
</td>
<td>
#Html.DisplayFor(modelItem => item.CompanyID)
</td>
<td>
#Html.DisplayFor(modelItem => item.Description)
</td>
<td>
#Html.DisplayFor(modelItem => item.InvoiceDate)
</td>
<td>
#Html.DisplayFor(modelItem => item.DueDate)
</td>
<td>
#Html.DisplayFor(modelItem => item.Paid)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id = item.InvoiceID }) |
#Html.ActionLink("Details", "Index", "InvoiceItem", new { id = item.InvoiceID }) |
#Html.ActionLink("Delete", "Delete", new { id = item.InvoiceID })
</td>
</tr>
}
</table>
A form only posts back the name/value pairs of its controls (input, textarea, select). Since the only 2 controls you generate are for the SelectedCompanyID and DisplayPaid properties of your model, then only those properties will be bound when post.
From your comments, what your really wanting to do is to update the table of invoices based on the values of the selected company and the checkbox.
From a performance point of view, the approach is to use ajax to update just the table of invoices based on the value of your controls.
Create a new controller method that return a partial view of the table rows
public PartialViewResult Invoices(int CompanyID, bool DisplayPaid)
{
// Get the filtered collection
IEnumerable<Invoice> model = DbContext.Invoice.Where(....
return PartialView("_Invoices", model);
}
Note you may want to make the CompanyID parameter nullable and adjust the query if your wanting to initially display unfiltered results
And a partial view _Invoices.cshtml
#model IEnumerable<yourAssembly.Invoice>
#foreach(var item in Model)
{
<tr>
<td>#Html.DisplayFor(m => item.InvoiceID)</td>
.... other table cells
</tr>
}
In the main view
#model yourAssembly.InvoiceViewModel
#Html.BeginForm()) // form may not be necessary if you don't have validation attributes
{
#Html.DropDownListFor(m => m.SelectedCompanyID, Model.Companies)
#Html.CheckboxFor(m => m.DisplayPaid)
<button id="filter" type="button">Filter results</button>
}
<table>
<thead>
....
</thead>
<tbody id="invoices">
// If you want to initially display some rows
#Html.Action("Invoices", new { CompanyID = someValue, DisplayPaid = someValue })
</tbody>
</table>
<script>
var url = '#Url.Action("Invoices")';
var table = $('#invoices');
$('#filter').click(function() {
var companyID = $('#SelectedCompanyID').val();
var isDisplayPaid = $('#DisplayPaid').is(':checked');
$.get(url, { CompanyID: companyID, DisplayPaid: isDisplayPaid }, function (html) {
table.append(html);
});
});
</script>
The alternative would be to post the form as your are, but rather than returning the view, use
return RedirectToAction("Invoice", new { companyID = model.SelectedCompanyID, DisplayPaid = model.DisplayPaid });
and modify the GET method to accept the additional parameter.
Side note: Your using the TagHelpers to generate
select asp-for="SelectedCompanyID" asp-items="Model.Companies" name="companyFilter" class="form-control"></select>
I'm not familiar enough with them to be certain, but if name="companyFilter" works (and overrides the default name which would be name="SelectedCompanyID"), then you generating a name attribute which does not match your model property and as a result SelectedCompanyID would be 0 (the default for int) in the POST method.
Appending ToList() to the statement that populates companies is converting the SelectList into a List<T>, which the form will not recognize as a SelectList. Also, by using the dynamic var keyword, you are masking this problem. Try this instead:
SelectList companies = new SelectList(await DbContext.Company.ToListAsync(), "CompanyID", "Name");
In general, try to avoid use of var unless the type is truly dynamic (unknown until runtime).
You put your model data out of form, so it would not submited!
<form asp-controller="Billing" asp-action="Index" method="post" class="form-horizontal" role="form">
<label for="companyFilter">Filter Company</label>
<select asp-for="SelectedCompanyID" asp-items="Model.Companies" name="companyFilter" class="form-control"></select>
<div class="checkbox">
<label>
<input type="checkbox" asp-for="DisplayPaid" />Display Paid
<input type="submit" value="Filter" class="btn btn-default" />
</label>
</div>
<br />
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Invoices.FirstOrDefault().InvoiceID)
</th>
<th>
#Html.DisplayNameFor(model => model.Invoices.FirstOrDefault().CompanyID)
</th>
<th>
#Html.DisplayNameFor(model => model.Invoices.FirstOrDefault().Description)
</th>
<th>
#Html.DisplayNameFor(model => model.Invoices.FirstOrDefault().InvoiceDate)
</th>
<th>
#Html.DisplayNameFor(model => model.Invoices.FirstOrDefault().DueDate)
</th>
<th>
#Html.DisplayNameFor(model => model.Invoices.FirstOrDefault().Paid)
</th>
<th></th>
</tr>
#foreach (var item in Model.Invoices)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.InvoiceID)
</td>
<td>
#Html.DisplayFor(modelItem => item.CompanyID)
</td>
<td>
#Html.DisplayFor(modelItem => item.Description)
</td>
<td>
#Html.DisplayFor(modelItem => item.InvoiceDate)
</td>
<td>
#Html.DisplayFor(modelItem => item.DueDate)
</td>
<td>
#Html.DisplayFor(modelItem => item.Paid)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id = item.InvoiceID }) |
#Html.ActionLink("Details", "Index", "InvoiceItem", new { id = item.InvoiceID }) |
#Html.ActionLink("Delete", "Delete", new { id = item.InvoiceID })
</td>
</tr>
}
</table>
</form>
Using a for loop to create the with the companies will make it possible to map back and persist the company values
for(c = 0 ; c < Model.Companies.Count(); c++)
{
<input type='hidden' name='#Html.NameFor(Model.Companies[c].Propery1)' id='#Html.IdFor(Model.Comapnies[c].Propery1)' value='somevalue'>someText />
<input type='hidden' name='#Html.NameFor(Model.Companies[c].Propery2)' id='#Html.IdFor(Model.Comapnies[c].Propery2)' value='somevalue'>someText />
}
this ensures that the list is mapped back as the default model binder expects list to be in ListProperty[index] format

ListBoxFor within IEnumerable model

My model -
...
public string[] _SelectedCountries { get; set; }
and my view -
#using System.Collections.Concurrent
#using Example.Models
#model IEnumerable<Example.Models.vw_SpecialQuestionDefinition>
#{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#using (Html.BeginForm("Create", "SpecialQuestionDefinition", FormMethod.Post))
{
#Html.AntiForgeryToken()
#Scripts.Render("~/Scripts/SpecialQuestions/Index.js")
<div class="row">
<div class="col-md-4">
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">Host Country</h4>
</div>
<div class="panel-body">
#*#Html.ListBoxFor("countries", null, new { #class = "sqQuestions" })*#
#Html.ListBoxFor(model => model._SelectedCountries,
new MultiSelectList((List<SelectListItem>)ViewData["countries"], "Value", "Text"),
new { style = "display:block;", #class = "sqQuestions" })
</div>
</div>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-5 col-md-12">
<input type="submit" value="Configure" class="btn btn-default"/>
</div>
</div>
<input type="hidden" name="specialQuestionsId" id="specialQuestionsId" value="-1" />
<input type="hidden" name="answerTypesId" id="answerTypesId" value="-1" />
<input type="hidden" name="hostCountrysId" id="hostCountrysId" value="-1" />
<input type="hidden" name="nationalitysId" id="nationalitysId" value="-1" />
<input type="hidden" name="scopeTypesId" id="scopeTypesId" value="-1" />
}
<h4>Special Question Definition</h4>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Question)
</th>
<th>
#Html.DisplayNameFor(model => model.AnswerType)
</th>
<th>
#Html.DisplayNameFor(model => model.LookupTable)
</th>
<th>
#Html.DisplayNameFor(model => model.CountryName)
</th>
<th>
#Html.DisplayNameFor(model => model.NationalityName)
</th>
<th>
#Html.DisplayNameFor(model => model.ScopeType)
</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Question)
</td>
<td>
#Html.DisplayFor(modelItem => item.AnswerType)
</td>
<td>
#Html.DisplayFor(modelItem => item.LookupTable)
</td>
<td>
#Html.DisplayFor(modelItem => item.CountryName)
</td>
<td>
#Html.DisplayFor(modelItem => item.NationalityName)
</td>
<td>
#Html.DisplayFor(modelItem => item.ScopeType)
</td>
</tr>
}
</table>
and within my controller -
List<SelectListItem> listCountriesSelectListItems = new List<SelectListItem>();
listSpecialQuestionsSelectListItems.Add(new SelectListItem() { Text = "All", Value = "-1" });
foreach (Country co in db.Countries.OrderBy(c => c.CountryName))
{
SelectListItem selectList = new SelectListItem()
{
Text = co.CountryName,
Value = co.CountryId.ToString()
};
listCountriesSelectListItems.Add(selectList);
}
ViewBag.countries = listCountriesSelectListItems;
so when I run the application, I get this error for my ListBoxFor -
'System.Collections.Generic.IEnumerable<Example.Models.vw_SpecialQuestionDefinition>' does not contain a definition for '_SelectedCountries' and no extension method '_SelectedCountries' accepting a first argument of type 'System.Collections.Generic.IEnumerable<Example.Models.vw_SpecialQuestionDefinition>' could be found (are you missing a using directive or an assembly reference?)
And I believe I understand why I get that error. Because I am trying to treat the model within the view as a single object, when in actuality it's of type IEnumerable<Example.Models.vw_SpecialQuestionDefinition>.
How do I get to _SelectedCountries from my model then?
Also, I use an IEnumerable model to populate a table beneath the list box. This list box is outside of the table and has no reason to be inside of it.
EDIT - POSTED entire view.
I am trying to treat the model within the view as a single object,
when in actuality it's of type
IEnumerable.
Your assumption is correct.
How do I get to _SelectedCountries from my model then?
You need a new model and add IEnumerable inside it.
public class SomeViewModel
{
public IEnumerable<vw_SpecialQuestionDefinition>
SpecialQuestionDefinitions { get; set; }
public string[] _SelectedCountries { get; set; }
}
Then foreach will finally like this -
#foreach (var item in Model.SpecialQuestionDefinitions)

MVC: Passing Value from View to Controller

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" })

Categories

Resources