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)
Related
#model IEnumerable<Calendar.Models.CheckDays
<p>
#using (Html.BeginForm())
{
<table>
<tr>
<th>
#Html.ActionLink("Create New", "Create")
</th>
<th>
#Html.DropDownListFor(model => model.DayOfWeek, htmlAttributes: new { #class = "form-control" })
</th>
<th>
<input type="button" value="Search" />
</th>
</tr>
</table>
}
</p>
<tr>
<th>
#Html.DisplayNameFor(model => model.DayOfWeek)
</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.DayOfWeek)
</td>
</td>
</tr>
}
I'm trying to create a drop down list so I can filter the the index results but I keep getting an error
"Compiler Error Message: CS1061: 'IEnumerable' does not
contain a definition for 'DayOfWeek' and no extension method
'DayOfWeek' accepting a first argument of type
'IEnumerable' could be found (are you missing a using
directive or an assembly reference?)"
The line that errors out is
Html.DropDownListFor(model => model.DayOfWeek, htmlAttributes: new { #class = "form-control" })".
Do I need to do something in the model or do I have syntax errors?
You misunderstood what first parameter is for. It is to point where selected item should go for. It is for selectedItem variable. Check this blog post: https://odetocode.com/blogs/scott/archive/2013/03/11/dropdownlistfor-with-asp-net-mvc.aspx. You have to create separate class for model with list of elements and variable SelectedDayOfWeek.
Model class:
public class CheckDaysViewModel
{
public IEnumerable<CheckDays> CheckDays {get;set;}
public IEnumerable<SelectListItem> CheckDaysAsSelectedList => this.CheckDays.Select(e => new SelectListItem(e.DayOfWeek, e.DayOfWeek));
public CheckDays SelectedDay {get;set;}
}
cshtml
#model CheckDaysViewModel
<p>
#using (Html.BeginForm())
{
<table>
<tr>
<th>
#Html.ActionLink("Create New", "Create")
</th>
<th>
#Html.DropDownListFor(m => m.SelectedDay, Model.CheckDaysAsSelectedList, null, htmlAttributes: new { #class = "form-control" })
</th>
<th>
<input type="submit" value="Search" />
</th>
</tr>
</table>
}
</p>
<table>
<tr>
<th>
#Html.DisplayNameFor(m => m.CheckDays.First().DayOfWeek)
</th>
</tr>
#foreach (var item in Model.CheckDays)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.DayOfWeek)
</td>
</tr>
}
</table>
I am a novice in asp.net and i want create simple database application.
I need pass parameters between view and controller to retrieve data from database.
i need only this data which title is "something". I create simple left menu which contains search settings.
This is my view page.
#model IEnumerable<TwojaBiblioteka.Models.Ksiazka>
#{
ViewBag.Title = "Home Page";
}
#Styles.Render("~/Content/css")
<div class="jumbotron">
<script src="~/Scripts/jquery-2.1.4.min.js"></script>
<script src="~/Scripts/jquery.unobtrusive-ajax.min.js"></script>
<div class="container">
<div class="row">
<div class="col-md-2">
<h2>Szukaj</h2>
<div class="textboxes">
<input type="text" name="Tytul" class="form-control" id="Tytul" placeholder="Tytuł..." />
<input type="text" name="txtAutor" class="form-control" id="txtAutor" placeholder="Autor..." />
<input type="text" name="txtISBN" class="form-control" id="txtISBN" placeholder="ISBN..." />
</div>
<center>
#Ajax.ActionLink("Szukaj", "SzukajKsiazki", new AjaxOptions()
{
HttpMethod="GET",
UpdateTargetId= "divKsiazki",
InsertionMode= InsertionMode.Replace
})
</center>
</div>
<div id="divKsiazki"class="col-md-10 ">
</div>
</div>
</div>
</div>
This is view for display data from database:
#model IEnumerable<TwojaBiblioteka.Models.Ksiazka>
<table class="table" style="border:1px solid black;">
<tr>
<th>
#Html.DisplayNameFor(model => model.Tytul)
</th>
<th>
#Html.DisplayNameFor(model => model.Autor)
</th>
<th>
#Html.DisplayNameFor(model => model.ISBN)
</th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Tytul)
</td>
<td>
#Html.DisplayFor(modelItem => item.Autor)
</td>
<td>
#Html.DisplayFor(modelItem => item.ISBN)
</td>
</tr>
}
</table>
And this is my controller:
public PartialViewResult SzukajKsiazki()
{
string tytul="something";
var ksiazkilist = db.Ksiazka.Where(x => x.Tytul == tytul).ToList();
return PartialView("_ListaKsiazek",wypozyczone);
}
So how i can pass data from my textboxes to controller to display only those records which contains textbox text?
Your controller action should accept a parameter. In Asp.Net MVC, it is normal for this to be the model:
public PartialViewResult SzukajKsiazki(IEnumerable<TwojaBiblioteka.Models.Ksiazka> model)
Your view should have all of the editor elements from the model enclosed in a form and you need a submit button:
#using (Html.BeginForm("SzukajKsiazki", "ControllerName", FormMethod.Post)
{
<table class="table" style="border:1px solid black;">
<tr>
<th>
#Html.DisplayNameFor(model => model.Tytul)
</th>
<th>
#Html.DisplayNameFor(model => model.Autor)
</th>
<th>
#Html.DisplayNameFor(model => model.ISBN)
</th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Tytul)
</td>
<td>
#Html.DisplayFor(modelItem => item.Autor)
</td>
<td>
#Html.DisplayFor(modelItem => item.ISBN)
</td>
</tr>
}
</table>
<input type="submit" value="submit">
}
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
I have 3 views (1 Index, 2 Contacts(partialview), 3 Details(partialview))
I have a database with 2 tables tied by ContactId that i can use to get the Details from the database to show. I used ADO to make a model of the database. The 2 tables (classes) are named Contact and ContactTelefon.
Instead of button I tried using #html.ActionLink (as u can see in Contact View) to get the Id from the row, but that takes me to a new page, and it doesn't even show details.
My question is: How could i get the details to show in textboxes so i can edit the data.
All actions must be in same view as far as the user is concerned.
Controller:
ContactsDbEntities db = new ContactsDbEntities();
[HttpGet] //Index
public ActionResult Index()
{
return View();
}
//Contacts
public ViewResult Contacts()
{
var contactsList = db.Contacts.ToList();
return View(contactsList);
}
//Details
public ActionResult Details(int? id)
{
ContactTelefon contactTel = db.ContactTelefons.Find(id);
return View(contactTel);
}
Index view
#using Demo.Models
#model Contact
#section scripts
{
<link href="~/Content/jquery-ui.min.css" rel="stylesheet" />
<script src="~/Scripts/jquery-ui.min.js"></script>
<script src="~/Scripts/jquery-ui.js"></script>
<script>
$(function () {
$(document).on('click', '#Details', function () {
$.get('#Url.Action("Details","Home")', function (data) {
$('#divDetails').replaceWith(data);
});
});
</script>
}
<table id="mainTable" class="table table-bordered table-striped">
<tr>
<th>
#Html.DisplayNameFor(model => model.ContactId)
</th>
<th>
#Html.DisplayNameFor(model => model.Nume)
</th>
<th>
#Html.DisplayNameFor(model => model.Prenume)
</th>
<th>
#Html.DisplayNameFor(model => model.Adresa)
</th>
<th>
#Html.DisplayNameFor(model => model.Mentiuni)
</th>
</tr>
<tr>
<th>
</th>
#using (Html.BeginForm())
{
<th>
#Html.TextBoxFor(model => model.Nume, null, new { id = "txtSearchNume", #class = "form-control" })
</th>
<th>
#Html.TextBoxFor(model => model.Prenume, null, new { id = "txtSearchPrenume", #class = "form-control" })
</th>
<th>
#Html.TextBoxFor(model => model.Adresa, null, new { id = "txtSearchAdresa", #class = "form-control" })
</th>
<th>
#Html.TextBoxFor(model => model.Mentiuni, null, new { id = "txtSearchMentiuni", #class = "form-control" })
</th>
<th>
<input type="submit" value="Create" class="btn btn-success"
onclick=" location.href='#Url.Action("Index", "Home")' " />
</th>
<th>
<input type="submit" name="submitSearch" value="Search" class="btn btn-info"
onclick=" location.href='#Url.Action("Create", "Home")' " />
</th>
<tr>
#{Html.RenderAction("Contacts", "Home");}
</tr>
<tr><div id="divDetails"></div></tr>
}
</table>
Contacts View
#using Demo.Models
#model IEnumerable<Contact>
<table class="table table-bordered table-hover">
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.ContactId)
</td>
<td>
#Html.DisplayFor(modelItem => item.Nume)
</td>
<td>
#Html.DisplayFor(modelItem => item.Prenume)
</td>
<td>
#Html.DisplayFor(modelItem => item.Adresa)
</td>
<td>
#Html.DisplayFor(modelItem => item.Mentiuni)
</td>
<td>
#Html.ActionLink("Delete", "Delete", new { id = item.ContactId },
new { #class = "btn btn-danger", onclick = "return confirm('Delete this record?');" })
</td>
<td>
<input id="Details" type="button" name="Details"
value="Details" class="btn btn-info" />
</td>
<td>
#Html.ActionLink("DetailsLink","Details",new{id = item.ContactId})
</td>
</tr>
}
</table>
Details View
#using Demo.Models
#model ContactTelefon
<div class="form-horizontal">
<div claass="form-group">
#* must get the id from Contacts *#
#Html.LabelFor(model => model.ContactId)
#Html.LabelFor(model => model.ContactTelefonId)
#Html.LabelFor(model => model.NumarTelefon)
#Html.LabelFor(model => model.TipNumarTelefon)
</div>
<br />
<div claass="form-group">
#Html.DisplayFor(model => model.ContactId)
#Html.DisplayFor(model => model.ContactTelefonId)
#Html.DisplayFor(model => model.NumarTelefon)
#Html.DisplayFor(model => model.TipNumarTelefon)
</div>
<div claass="form-group">
#Html.EditorFor(model => model.ContactId)
#Html.EditorFor(model => model.ContactTelefonId)
#Html.EditorFor(model => model.NumarTelefon)
#Html.EditorFor(model => model.TipNumarTelefon)
</div>
</div>
It seems as if you're starting MVC coming from ASP.NET WebForms. The thing about MVC is that it doesn't do any magic like WebForms so you have to have a good understanding of what happens behind the scenes to be able to make a smooth transition. Also, from the looks of it your database model uses Entity Framework.
First off the way you're handling the Details button is all wrong. What you should be doing is this:
HTML
<input type="button" name="Details" value="Details" class="btn btn-info js-details"
data-id="#item.ContactId" />
JavaScript
$(document).on('click', '.js-details', function (event) {
// get the element that triggered the event
var $element = $(event.currentTarget);
var id = $element.data('id');
// you might have to type in the literal URL if you have a custom route
// here
$.get('#Url.Action("Details","Home")'+ '?id=' + id, function (data) {
$('#divDetails').html(data);
});
});
Let me know if this works for you. There are other things that you can improve but this should be a pretty good start.
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" })