Reading collection of user input from the view? - asp.net-mvc

I have created a page to display list of questions to user & read user input for each question.
Issue: When user submits the Form, Model Parameter is null.
Post Action of the View is defined as below -
[HttpPost]
public ActionResult Index(IEnumerable<Question> model)
{
return View(model);
}
Code to display options to user is as below -
#foreach (var quest in Model)
{
<li>
#Html.LabelFor(x => quest.Title, quest.Title)
<ol class="Opt">
#foreach (var opt in quest.Options)
{
<li>#Html.RadioButtonFor(o => opt, opt.Title)
#Html.LabelFor(o => opt.Title, opt.Title)
</li>
}
</ol>
</li>
}
Here model is #model List<LakshyaMvc.Models.Question> and of course I have wrapped the code inside #using (Html.BeginForm())

Change your View Model.
#using (Html.BeginForm("Index", "Queries", FormMethod.Post))
{
for (int i = 0; i < Model.Count; i++)
{
<li>
#Html.LabelFor(x => Model[i].Question, Model[i].Question)
<ol class="Opt">
#for (int j = 0; j < Model[i].Options.Count; j++)
{
<li>#Html.RadioButtonFor(o => Model[i].Options[j].Option, false)
#Html.LabelFor(o => Model[i].Options[j].Option,
Model[i].Options[j].Option)
</li>
}
</ol>
</li>
<input type="submit" name="Submit" value="Submit" />
}
}

Related

How do I update a specific database entry with a view model as controller method attribute

I have an input table in my website which is connected to a View Model. In the controller method, I pass this View Model to the controller and vice versa, meaning the controller populates the view model with data from the data base and the view returns a view model populated with form data the user might have entered.
The problem is that once the view received the view model object, the "ID" attribute from the database is no longer there. When the Post method is called, there is no way to know which database entry must be updated.
My question is: How do I update a specific database entry when I pass a view model to the controller method?
Example Controller Method:
[HttpPost]
public ActionResult method(ViewModel vm)
{
DataContext.Context.Where(x => x.ID == vm.Object.ID) // this is where vm.Object.ID always returns "0", not the actual ID from the database entry
Context.SaveChanges();
return View(vm);
}
If you need more information, please let me know. Also, using jquery is not a viable option for this project. Thanks a lot for your help!
Edit:
View:
#model MyANTon.ViewModels.Q4_Answer_VM
#{
ViewBag.Title = "myANTon Anforderungserfassung";
ViewBag.HideNavBar = false;
}
#using (Html.BeginForm())
{
<div class="container">
<div class="jumbotron">
<hr />
<table class="grid" id="datatable">
<tr>
<th>Nr.</th>
<th>Last</th>
<th>Quelle</th>
<th>Ziel</th>
<th>Frequenz [/h]</th>
<th>Abstand [m]</th>
<th></th>
<th></th>
#{int i = 1; }
#for (var a = 0; a < Model.Matrix.Count; a++)
{
<tr>
<td>#(i++)</td>
<td>#Html.TextBoxFor(x => Model.Matrix[a].Load)</td>
<td>#Html.TextAreaFor(x => Model.Matrix[a].Source)</td>
<td>#Html.TextAreaFor(x => Model.Matrix[a].Goal)</td>
<td>#Html.TextAreaFor(x => Model.Matrix[a].Frequency)</td>
<td>#Html.TextAreaFor(x => Model.Matrix[a].Distance)</td>
<td><input type="submit" name="+" class="btn btn-default" value="+" /></td>
<td><input type="submit" class="btn btn-default" value="-" /></td>
</tr>
}
</table>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-default" name="Speichern" value="Speichern" />
<input type="submit" class="btn btn-default" value="Speichern und weiter" />
<input type="button" class="btn btn-default" value="Weiter zu Schritt 5" onclick="#("window.location.href='" + #Url.Action("Q_Fifthpage", "Home") + "'");" />
</div>
</div>
</div>
</div>
}
GET Method:
[HttpGet]
public ActionResult Q_FourthPage()
{
// get current Questionnaire ID
int CurrentQstID = Convert.ToInt32(Session["qstid"]);
// create vm object. Capacity is a column in the table.
var Q4ViewModel = new ViewModels.Q4_Answer_VM();
// look for existing input data columns for this questionnaire in db
if (db.Capacities.Any(x => x.Questionnaire_ID == CurrentQstID))
{
// answers exist
Q4ViewModel.Matrix.AddRange(db.Capacities.Where(x => x.Questionnaire_ID == CurrentQstID));
}
else
{
// new capacity matrix
Q4ViewModel.TMatrix = db.QuestionTexts.Where(x => x.ID == 21).FirstOrDefault();
Q4ViewModel.Matrix = new List<Models.Capacity>();
}
var tmpcapacity = new Models.Capacity();
tmpcapacity.Questionnaire_ID = Convert.ToInt32(Session["qstid"]);
Q4ViewModel.Matrix.Add(tmpcapacity);
db.Capacities.Add(tmpcapacity);
db.SaveChanges();
return View(Q4ViewModel);
}
POST Method:
[HttpPost]
public ActionResult Q_FourthPage(ViewModels.Q4_Answer_VM vm)
{
int currentQst = Convert.ToInt32(Session["qstid"]);
if (Request.Form["+"] != null)
{
var tmpcapacity = new Models.Capacity();
tmpcapacity.Questionnaire_ID = currentQst;
vm.Matrix.Add(tmpcapacity);
db.Capacities.Add(tmpcapacity);
db.SaveChanges();
return View(vm);
}
if (Request.Form["Speichern"] != null)
{
// save data
if (!ModelState.IsValid) return View("~/Views/Shared/Error.cshtml");
var tmpcapacity = new Models.Capacity();
for (var a = 0; a < vm.Matrix.Count; a++)
{
var current = vm.Matrix[a];
current.ID = vm.Matrix[a].ID;
if (db.Capacities.Any(x => x.ID == current.ID))
// if clause never triggers true
// vm does not contain capacity ID
{
// column exists and is changed (or not)
tmpcapacity.Distance = vm.Matrix[a].Distance;
tmpcapacity.Frequency = vm.Matrix[a].Frequency;
tmpcapacity.Source = vm.Matrix[a].Source;
tmpcapacity.Goal = vm.Matrix[a].Goal;
tmpcapacity.Load = vm.Matrix[a].Load;
Models.Capacity c = db.Capacities.Where(x => x.ID == current.ID).FirstOrDefault();
c = tmpcapacity;
db.SaveChanges();
}
else
{
// new column
tmpcapacity.Distance = vm.Matrix[a].Distance;
tmpcapacity.Frequency = vm.Matrix[a].Frequency;
tmpcapacity.Source = vm.Matrix[a].Source;
tmpcapacity.Goal = vm.Matrix[a].Goal;
tmpcapacity.Load = vm.Matrix[a].Load;
tmpcapacity.Questionnaire_ID = currentQst;
db.Capacities.Add(tmpcapacity);
db.SaveChanges();
}
}
db.SaveChanges();
}
return View(vm);
}
If you need to bind the ID to the Model then you need to use hidden filed under the form when you are using Razor.
#Html.HiddenFor(model => model.Id)
For more details
#using (Html.BeginForm("method", "ControllerName", FormMethod.Post))
{
#Html.HiddenFor(Model=>Model.ID)
<div class="form-group">
#Html.LabelFor(m => m.Email, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.Email, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-default" value="Button" />
</div>
</div>
}
Now you can access ID at your controller action method.
If you want to "save" the object ID and get it back when the post occurs, you need to store it into a hidden field using the .HiddenFor() HTML helper - something like this:
#using (Html.BeginForm())
{
#Html.HiddenFor(m => m.Object.Id);
<div class="container">
<div class="jumbotron">
Then, upon your POST, you should get back the Object.ID in your post body and you should be able to tell which object this is for.

#Html.HiddenFor returning null value

I am trying to return the results of a table back to the controller for further manipulation. Once returned to the controller the value shows as null. In the past I have been able to use #Html.HiddenFor to return the values but it doesn't seem to be working in this instance. Not sure what I am doing wrong here. Any help is greatly appreciated.
#model IEnumerable<Project.Models.Item>
#{
ViewBag.Title = "Welcome to The Project";
}
#using (Html.BeginForm("UpdateQuality", "Home", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
<div class="row">
<div class="form-group">
<table class="table table-bordered">
<tr>
<th>#Html.DisplayNameFor(m => m.Name)</th>
<th>#Html.DisplayNameFor(m => m.SellIn)</th>
<th>#Html.DisplayNameFor(m => m.Quality)</th>
</tr>
#for (int i = 0; i < Model.Count(); i++)
{
<tr>
<td>#Html.DisplayFor(m => m.ElementAt(i).Name)</td>
<td>#Html.DisplayFor(m => m.ElementAt(i).SellIn)</td>
<td>#Html.DisplayFor(m => m.ElementAt(i).Quality)</td>
#Html.HiddenFor(m => m.ElementAt(i).Name)
#Html.HiddenFor(m => m.ElementAt(i).SellIn)
#Html.HiddenFor(m => m.ElementAt(i).Quality)
</tr>
}
</table>
<div class="form-group">
<div style="margin-top: 50px">
<input type="submit" class="btn btn-primary" value="Advance Day"/>
</div>
</div>
</div>
</div>
}
And here is the controller which returns null.
public ActionResult UpdateQuality(List<Item> Items )
{
return View("Index", (object)Items);
}
You cannot use ElementAt() in a HtmlHelper method that generates form controls (look at the name attribute your generating - it does not match your model).
Either change the model to be IList<T>
#model List<Project.Models.Item>
and use a for loop
#for (int i = 0; i < Model.Count; i++)
{
....
#Html.HiddenFor(m => m.[i].Name)
....
or change use a custom EditorTemplate for typeof Item, and in the main view, use #Html.EditorFor(m => m) to generate the correct html for each item in the collection.

Paging issue in ASP.Net Mvc Application

I am developing an MVC application in which I use a DropdownList to select "vendor". When I select a vendor from the dropdown then the view shows products related to the selected vendor.
I use paging to display multiple pages of particular vendor's products.
My issue is when I select a vendor from dropdown, at change event it displays products on all pages properly. If I select 2nd page it shows products from 2nd page. But next time if I select another vendor from dropdown, it shows 2nd page of selected vendor. But what I want is to display first page of selected vendor initially.
Controller code as below
public ActionResult Index(int? page ,int VendorId = 0)
{
var pageNumber = (page ?? 1);
var pagesize = 2;
if (VendorId == 0)
{
VendorId = Convert.ToInt32(Session["InventoryVendorId"]);
}
VendorService vendorService = new VendorService();
SelectList SelectList = new SelectList(vendorService.GetAll().OrderBy(t => t.Name), "Id", "Name", VendorId);
ViewData["list"] = SelectList;
int id = Convert.ToInt32(Session["loggedEmpId"]);
CommonService.SetEmployeeId(id);
if (VendorId != 0)
{
Session["InventoryVendorId"] = VendorId;
ProductService ProductService = new ProductService();
var productList = ProductService.GetProductInventory().Where(x=>x.VendorId == VendorId);
return View(productList.ToPagedList(pageNumber, pagesize));
}
else
{
return View();
}
}
code for view as below
#model PagedList.IPagedList<StockWatch.DTO.ProductDTO>
#using PagedList.Mvc;
#using System.Web.UI.WebControls
#{
ViewBag.Title = "Index";
int VendorId = Convert.ToInt32(Session["InventoryVendorId"]);
}
<link href="~/Content/PagedList.css" rel="stylesheet" />
<div class="row-fluid">
<div id="vendorDropdownDiv4" class="span12 " style="margin-left:0px;margin-top:10px;">
<div class="span6" >
<div class="span4" style="margin-left:1px;" >
<label >Vendor</label>
</div>
<div class="span6" >
#Html.DropDownList("VendorId", ViewData["list"] as SelectList, "-- Select vendor --", new { #id = "vendorIdforInventory", #name = "VendorId" })
</div>
</div>
<div class="span3" style="text-align:right">
#* <input class="btn btn-primary" type="submit" value="Load" id="create"/>*#
#*<input class="btn btn-default" value="Cancel" style="width:45px;" onclick="window.location.href='#Url.Action("index") '"/>*#
</div>
</div>
</div>
<div id="Newindexview"></div>
#if(Model != null)
{
</div>
<div class="span12" style="margin-left:0px;">
<table>
<thead>
<tr >
<th style="width:250px;" >Product Name
</th>
<th style="width:180px; text-align:left;" >Product Code
</th>
<th style="border-right: solid #e8eef4 thick; width: 0px; text-align:right;">Avg. Weight
</th>
#{
foreach (var location in ViewBag.loc)
{
<th style="width:10px;text-align:right;">#location.Name</th>
}
}
</tr>
</thead>
<tbody>
#foreach (var p in Model)
{
<tr>
<td style="width:250px;">
#p.Name
</td>
<td style="width:180px;text-align:left;">
#p.ProductCode
</td>
<td style="border-right: solid #e8eef4 thick; width: 15px; text-align:right">
#p.AvgWeight
</td>
#foreach (var location in ViewBag.loc)
{
flag = false;
if(p.Inventory != null)
{
foreach (var loc in p.Inventory)
{
if (location.Name == loc.LocationName)
{
<td style="width:10px; text-align:right;">#loc.Quantity</td>
flag = true;
}
}
}
if (flag == false)
{
<td style="width:10px; text-align:right;">0</td>
}
}
</tr>
}
</tbody>
</table>
</div>
<div class="span12" style="margin-left:0px;">
<div class="span6" style="margin-left:0px;">
#Html.PagedListPager(Model, page => Url.Action("Index", new {page ,searchContent=searchcontent}))
</div>
</div>
</div>
}
and jquery code as below
$("#vendorIdforInventory").change(function () {
var vendorid = $('#vendorIdforInventory').val();
$.ajax({
url: '#Url.Action("Index", "Inventory")',
data: {
VendorId: vendorid
},
type: "POST",
success: function (data) {
location.reload();
$('#modeldiv1').empty();
$('#vendorDropdownDiv4').hide();
$('#Newindexview').html("");
$('#Newindexview').html(data);
}
});
});
How to solve this paging issue?
You need to pass the currentFilter from your view back into your controller (which you have with VendorID), and set the page accordingly. Details are in the ASP.NET tutorial on paging at http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/sorting-filtering-and-paging-with-the-entity-framework-in-an-asp-net-mvc-application.
Look at how they've implemented filtering using the searchString and currentFilter variable, that's what will make sure that when a new string is entered (or selected from a drop down list in your case) that the paging responds accordingly.
Specifically, this is what I think you're missing in your code
if (searchString != null)
{
page = 1;
}
else
{
searchString = currentFilter;
}
...
int pageNumber = (page ?? 1);

Partial View with self updating

I'm learning ASP.NET MVC and I've encountered a problem in my practice project (MVC Music Store). I have made a partial view to search for an artist. I expect the partial view to take no arguments and work on its own.
Partial view in the right half part
I have a view specific model for the Artist Search Partial View. The model is as follows:
public class ArtistSearch
{
public string SearchString { get; set; }
public List<Artist> SearchResult { get; set; }
public ArtistSearch()
{
SearchResult=new List<Artist>();
}
}
Controller code is as follows:
public ActionResult Search(string query)
{
ArtistSearch asResult = new ArtistSearch();
if (query != null)
{
var temp = from a in db.Artists
where a.Name.Contains(query)
select a;
asResult.SearchResult = temp.ToList();
asResult.SearchString = query;
}
return PartialView(asResult);
}
The Partial View is as follows:
#model MvcMusicStore.Models.ArtistSearch
<div class="big-search-box">
<form action="#Url.Action("Search","Artist")" method="post" role="form">
<div class="input-group">
#Html.TextBox("query", #Model.SearchString, new { #class = "form-control nrb input-lg", placeholder = "Input your search query..." })
<div class="input-group-btn">
<input type="submit" name="Send" value="Search" class="btn btn-primary btn-iconed btn-lg" />
</div>
</div>
</form>
</div>
<div class="big-search-result-info clearfix">
<div class="pull-left">Showing results for <strong>#Model.SearchString</strong>.</div>
<div class="pull-right"><strong>#Model.SearchResult.Count</strong> artist(s) found.</div>
</div>
<table>
#foreach (var item in #Model.SearchResult)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
<a href="#item.Id" >
<img src=#item.PhotoURL alt=#item.Name style="width:100px;height:70px;">
</a>
</td>
</tr>
}
</table>
I wish to place this partial view anywhere on the site. Lets say i placed it(using RenderAction) on Artist/Index Controllers View page.
The simple functionality that I'm trying to achieve is that when i click on search it should self update the partial view with search results. Right now it is transferring me to Artist/Search page.
Thanks for the patience.
Try to use Ajax.BeginForm, below is an example:
Action
public ActionResult Search(string query)
{
ArtistSearch asResult = new ArtistSearch();
if (query != null)
{
var temp = from a in db.Artists
where a.Name.Contains(query)
select a;
asResult.SearchResult = temp.ToList();
asResult.SearchString = query;
}
return PartialView("MyParitalView",asResult);
}
View
<script src="~/Scripts/jquery-1.10.2.min.js"></script>
<script src="~/Scripts/jquery.unobtrusive-ajax.js"></script>
#using (Ajax.BeginForm("Search", "Home", new AjaxOptions { UpdateTargetId = "result" }))
{
<div class="input-group">
#Html.TextBox("query", #Model.SearchString, new { #class = "form-control nrb input-lg", placeholder = "Input your search query..." })
<div class="input-group-btn">
<input type="submit" name="Send" value="Search" class="btn btn-primary btn-iconed btn-lg" />
</div>
</div>
}
<div id="result"></div>
Parital View : MyParitalView
#model MvcMusicStore.Models.ArtistSearch
<div class="big-search-result-info clearfix">
<div class="pull-left">Showing results for <strong>#Model.SearchString</strong>.</div>
<div class="pull-right"><strong>#Model.SearchResult.Count</strong> artist(s) found.</div>
</div>
<table>
#foreach (var item in #Model.SearchResult)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
<a href="#item.Id">
<img src=#item.PhotoURL alt=#item.Name style="width:100px;height:70px;">
</a>
</td>
</tr>
}
</table>

ASP.NET MVC Binding to a Dictionary in View

I have an ASP.NET MVC 3 application that binds data to a Model. Simple objects within the Model are working perfectly like: ContactName and Telephone.
I am doing this by simply having: #Html.TextBoxFor(m => m.ContactName)
The Model contains a Dictionary object which isn't getting binded with my current implementation.
Can anyone give me an example of how I can do this? The current code I have for binding to a Dictionary is:
<div>
#*TABS*#
<ul class="nav nav-tabs">
#for (var i = 0; i < Model.Translations.Count; i++)
{
<li class="#i == 0 ? 'active' : ''">#Model.Translations.Keys.ToList()[i]</li>
}
</ul>
#*TABCONTENT*#
<div class="tab-content" style="overflow: visible;">
#foreach (var translation in Model.Translations)
{
for (var i = 0; i < Model.Translations.Count; i++)
{
<div class="#i == 0 ? 'active' : ''" id="#translation.Value.CultureCode">#translation.Value.Title</div>
}
#Html.TextBoxFor(m => translation.Value.Title[]);
#Html.TextBoxFor(m => translation.Value.FullDescription);
#Html.TextBoxFor(m => translation.Value.PreviewDescription);
}
</div>
</div>
Any help is highly appreciated.
use "your list property"[index].value.id
example
if you have:
public IList<KeyValuePair<int,string>> Properties { get; set; }
you should write in view:
for (int i = 0; i < Model.Properties.Count; i++)
{
#Html.EditorFor(item=>Model.Properties[i].Value)
}
updated:
#for (var i=0; i < Model.Translations.Count; i++) {
<div id="tabs-#(i)">
<div class="#i == 0 ? 'active' : ''" id="#Model.Translations[i].Value.CultureCode">#Model.Translations[i].Value.Title</div>
#Html.TextBoxFor(m => Model.Translations[i].Value.Title);
#Html.TextBoxFor(m => Model.Translations[i].Value.FullDescription);
#Html.TextBoxFor(m => Model.Translations[i].Value.PreviewDescription); }
</div>
</div>
</div>
Try this, I am considering translation is the Dictionary Object:
#foreach (var category in translation) {
#Html.TextBoxFor(m => category.Value.Title);
#Html.TextBoxFor(m => category.Value.FullDescription);
#Html.TextBoxFor(m => category.Value.PreviewDescription);
}

Resources