I'm working on a MVC3 ASP.Net application. I'm trying to figure out how to set the Quantity variable so when I pass it through to the controller with Html.ActionLink it has the correct number. Here's the view's code
#model IEnumerable<GreatVideosTrainingApplication.Models.Candy>
#{
ViewBag.Title = "Great Videos";
List<GreatVideosTrainingApplication.Models.Candy> candies = new List<GreatVideosTrainingApplication.Models.Candy>();
foreach (var candy in Model)
{
candies.Add(candy);
}
var grid = new WebGrid(candies);
var Quantity = 0;
}
<p>Welcome To Great Videos! The best source for your favorite DVDs and Blu-Rays</p>
<img src ="/Content/Images/dvd50.jpg" />
<p></p>
<img src="/Content/Images/bluray.jpg" />
<form method="post" action="/ShoppingCart/AddToCandyCart/"+item.CandyID >
#grid.GetHtml(#columns: grid.Columns(
grid.Column("Name"),
grid.Column("Price"),
grid.Column("Quantity", format: (item) => #Html.TextBox("Quantity", #Quantity)),
grid.Column("AddToCart", format: (item) => Html.ActionLink("Add To Cart", "AddToCandyCart", "ShoppingCart", new { id = item.CandyID, quantity = #Quantity }, ""))
)
)
</form>
I'm trying to set the value for the quantity with the Html.TextBox but it's not working. Keep in mind here I don't know javascript, and I'm extremely new to MVC3. Any and all help is greatly appreciated though.
public ActionResult AddToCandyCart(int id, FormCollection values)
{
// Add it to the shopping cart
var quantity = values["Quantity"];
var cart = ShoppingCart.GetCart(this.HttpContext);
// Retrieve the video from the database
var addedCandy = storeDB.Candies.Single(Candy => Candy.CandyID == id);
cart.AddToCandyCart(addedCandy, int.Parse(quantity));
// Go back to the main store page for more shopping
return RedirectToAction("Index");
}
The following code worked for me when including a textbox. I had trouble with the html helpers so I just wrote the code for the input box directly. I hope this helps.
grid.Column("Quantity", format: #<text><input name="Quantity" type="text" value="#item.Quantity"</text>))
Figured out the problem from mixing a variety of sources. Wanted to thank everyone. Here's the view.
#model IEnumerable<GreatVideosTrainingApplication.Models.Candy>
#{
ViewBag.Title = "Great Videos";
List<GreatVideosTrainingApplication.Models.Candy> candies = new List<GreatVideosTrainingApplication.Models.Candy>();
foreach (var candy in Model)
{
candies.Add(candy);
}
var grid = new WebGrid(candies);
var Quantity = 0;
}
<p>Welcome To Great Videos! The best source for your favorite DVDs and Blu-Rays</p>
<img src ="/Content/Images/dvd50.jpg" />
<p></p>
<img src="/Content/Images/bluray.jpg" />
<form method="post" action="../ShoppingCart/AddToCandyCart/" >
#using (Html.BeginForm()) {
#grid.GetHtml(#columns: grid.Columns(
grid.Column("Name"),
grid.Column("Price"),
grid.Column("Quantity", format: #<text><input name="Quantity" type="text" value="#Quantity"</text>),
grid.Column("AddToCart", format: #<text><input type="submit" value="Add To Cart" name="submit" /></text>)
)
)
}
</form>
Here's the Action Controller
[HttpPost]
public ActionResult AddToCandyCart(FormCollection values)
{
int id = 1;
string[] quantities = values["Quantity"].Split(',');
foreach (var item in quantities)
{
try
{
int quantity = int.Parse(item);
if (quantity >= 1)
{
// Add the candy to the shopping cart
var addedCandy = storeDB.Candies.Single(Candy => Candy.CandyID == id);
var cart = ShoppingCart.GetCart(this.HttpContext);
cart.AddToCandyCart(addedCandy, int.Parse(item));
}
}
catch (Exception e)
{
return View("Failed");
}
id++;
}
// Go back to the main store page for more shopping
return RedirectToAction("Index");
}
Related
I am trying to filter a list view using a dropdown as a filter.
My controller:
public async Task<ActionResult> Index(int? TradeExerciseNumber)
{
var TradeExerciseEntries = new TradeExerciseController().GetAll();
ViewBag.TradeExerciseEntries = new SelectList(TradeExerciseEntries, "TradeExerciseID", "TradeExerciseNumber");
if (TradeExerciseNumber != null)
{
return View(await db.TradesModels.Where(x => x.TradeExerciseId == TradeExerciseNumber).ToListAsync());
}
return View(await db.TradesModels.ToListAsync());
}
And my view:
#using (Html.BeginForm())
{
<p>
Find by Exercise Number: #Html.DropDownList("TradeExerciseEntries", -how do I pass value to TradeExerciseNumber in my controller to let it render pls- )
<input type="submit" value="Search" />
</p>
}
Now, how do I pass the dropdownlist value to TradeExerciseNumber in my controller to let it render please? Thank you very much.
Best regards
So this is what I did in my view and it worked:
#using (Html.BeginForm("Index", "Trades")){
<p>
Find by Exercise Number: #Html.DropDownList("TradeExerciseNumber", ViewBag.TradeExerciseEntries as SelectList, null, new { onchange = "submit();" })
</p>
}
I hope it helps. Thanks
I have numerous data displayed in a table, let's say a long list of users (first name & last name), so I set up a paging feature to display the elements by pages via the PagedList NuGet package. I was inspired by this tutorial: https://learn.microsoft.com/en-us/aspnet/mvc/overview/getting-started/getting-started-with-ef-using-mvc/sorting-filtering-and-paging-with-the-entity-framework-in-an-asp-net-mvc-application
I implemented a drop-down list in my view, so that I can directly choose the number of elements to display per page. I managed to include a jQuery script that makes the page size update whenever the drop-down list has a new selected value.
Using the mentioned tutorial , I also added a search feature: submitting a string in a search form allows to filter the data.
My problem is: changing the page size by selecting a new value in the drop-down list after having done a search doesn't work: the search results are reset, all the entries being displayed instead. I guess I forgot to pass some parameter somewhere but I just can't figure out where...
Here is my controller:
public ActionResult Index(string sortOrder, string currentFilter, string searchString, int? page, int? PageSize)
// Sort order is passed to view in order to keep it intact while clicking in another page link
ViewBag.CurrentSort = sortOrder;
// Ascending or descending sorting by first or last name according to sortOrder value
ViewBag.LastNameSortParm = String.IsNullOrEmpty(sortOrder) ? "lastname_desc" : "";
ViewBag.FirstNameSortParm = sortOrder == "firstname" ? "firstname_desc" : "firstname";
// Not sure here
if (searchString == null)
{
searchString = currentFilter;
}
// Pass filtering string to view in order to maintain filtering when paging
ViewBag.CurrentFilter = searchString;
var users = from u in _db.USER select u;
// FILTERING
if (!String.IsNullOrEmpty(searchString))
{
users = users.Where(u => u.lastname.Contains(searchString)
|| u.firstname.Contains(searchString)
}
// Ascending or descending filtering by first/last name
switch (sortOrder)
{
case "lastname": // Ascending last name
users = users.OrderBy(u => u.lastname);
break;
case "lastname_desc": // Descending last name
users = users.OrderByDescending(u => u.lastname);
break;
case "firstname": // Ascending first name
users = users.OrderBy(u => u.firstname);
break;
case "firstname_desc": // Descending first name
users = users.OrderByDescending(u => u.firstname);
break;
default:
users = users.OrderBy(u => u.lastname);
break;
}
// DROPDOWNLIST FOR UPDATING PAGE SIZE
int count = _db.USER.OrderBy(e => e.Id).Count(); // Total number of elements
// Populate DropDownList
ViewBag.PageSize = new List<SelectListItem>() {
new SelectListItem { Text = "10", Value = "10", Selected = true },
new SelectListItem { Text = "25", Value = "25" },
new SelectListItem { Text = "50", Value = "50" },
new SelectListItem { Text = "100", Value = "100" },
new SelectListItem { Text = "All", Value = count.ToString() }
};
int pageNumber = (page ?? 1);
int pageSize = (PageSize ?? 10);
ViewBag.psize = pageSize;
return View(users.ToPagedList(pageNumber, pageSize));
}
And my Index.cshtml view:
<script src="~/Scripts/jquery-3.2.1.js" type="text/javascript"></script>
<script type="text/javascript">
$(function () { // Submit pageSizeForm when another pageSize value is selected
$("#pageSize").change(function () {
$("#pageSizeForm").submit();
});
});
</script>
#model PagedList.IPagedList<AfpaSIPAdmin.Models.USER>
#using PagedList.Mvc;
<link href="~/Content/PagedList.css" rel="stylesheet" type="text/css" />
#{
ViewBag.Title = "Users management";
}
<h1>Users management</h1>
<!-- Creating a new entry in table -->
<p>
#Html.ActionLink("Create new user", "Create")
</p>
<!-- Filtering table entries -->
#using (Html.BeginForm("Index", "Users", FormMethod.Get, new { id = "filterForm" }))
{
<p>
Filter: #Html.TextBox("SearchString", ViewBag.CurrentFilter as string, new { #placeholder = "First or last name..." })
<input type="submit" value="Apply"/>
</p>
}
<!-- Display table -->
<table class="table">
<tr>
<th>
#Html.ActionLink("Last name", "Index", new {
sortOrder = ViewBag.LastNameSortParm,
currentFilter = ViewBag.CurrentFilter
})
</th>
<th>
#Html.ActionLink("First name", "Index", new {
sortOrder = ViewBag.FirstNameSortParm,
currentFilter = ViewBag.CurrentFilter
})
</th>
<th style="min-width: 170px"></th>
</tr>
#foreach (var item in Model)
{
<tr>
<td style = "min-width: 150px">
#Html.DisplayFor(modelItem => item.lastname)
</td>
<td style = "min-width: 150px">
#Html.DisplayFor(modelItem => item.firstname)
</td>
<td> <!-- Using images as buttons for actions -->
<a href="#Url.Action("Edit", "Users", new { id = item.Id })" title="Edit">
<img src="~/Content/images/edit.gif" />
</a>
<a href="#Url.Action("Details", "Users", new { id = item.Id })" title="Details">
<img src="~/Content/images/info.gif" />
</a>
<a href="#Url.Action("Delete", "Users", new { id = item.Id })" title="Delete">
<img src="~/Content/images/delete.gif" />
</a>
</td>
</tr>
}
</table>
<br/>
<!-- Paging -->
#using (Html.BeginForm("Index", "Users", FormMethod.Get, new { id = "pageSizeForm" }))
{
<div class="pager">
Page #(Model.PageCount < Model.PageNumber ? 0 : Model.PageNumber) sur #Model.PageCount<br/>
#Model.Count of #Model.TotalItemCount elements
#Html.PagedListPager(Model, page => Url.Action("Index", new {
page,
sortOrder = ViewBag.CurrentSort,
currentFilter = ViewBag.CurrentFilter,
searchString = ViewBag.CurrentFilter,
pageSize = ViewBag.psize
}))
<!-- DropDownList for setting page size -->
Elements per page :
#Html.DropDownList("pageSize")
</div>
}
The reason is because you have 2 forms. When you submit the first form containing the textbox, the only value you send back to the controller is SearchString and all the other parameters in your method will be their default (for example when you return the view, PageSize will default to null and therefore return only 10 records even if the user previously selected say 50.
Likewise, when you submit the 2nd form containing dropdownlist for the page size, the value of SearchString will be null because its not sent in the request.
You need to have one form only containing both form controls. And if you wanted to send additional properties, for example the current sort order, then you can add those as query string values in the form element (for example, #using(Html.BeginForm("Index", "Users", new { sortOrder = .... }, FormMethod.Get))
I would also strongly recommend you use a view model containing the properties you need in the view and strongly bind to them rather than using ViewBag
public class UsersVM
{
public string SearchString { get; set; }
public int PageSize { get; set; }
public IEnumerable<SelectListItem> PageSizeOptions { get; set; }
.....
public IPagedList<USER> Users { get; set; }
}
View
#model UsersVM
...
#using(Html.BeginForm("Index", "Users", FormMethod.Get))
{
#Html.LabelFor(m => m.SearchString)
#Html.TextBoxFor(m => m.SearchString)
#Html.LabelFor(m => m.PageSize)
#Html.DropDownListFor(m => m.PageSize, Model.PageSizeOptions)
<input type="submit" value="Filter" />
}
....
<div class="pager">
Page #(Model.Users.PageCount < Model.Users.PageNumber ? 0 : Model.Users.PageNumber)
....
#Html.PagedListPager(Model.Users, page => Url.Action("Index", new {
page,
sortOrder = Model.CurrentSort,
currentFilter = Model.CurrentFilter,
searchString = Model.CurrentFilter,
pageSize = Model.PageSize
}))
</div>
and in the controller method, initialize a new instance of UsersVM and assign its properties
public ActionResult Index(string sortOrder, string currentFilter, string searchString, int? page, int? pageSize)
{
UsersVM model = new UsersVM();
....
var users = from u in _db.USER select u;
....
pageSize = pageSize ?? 10;
model.PageSize = pageSize.Value;
model.Users = users.ToPagedList(pageNumber, pageSize);
model.PageSizeOptions = new List<SelectListItem> { .... };
return View(model);
}
I am working on a simple image upload site in which users will have the ability to post comments on the images uploaded to the site, whenever posting a comment I am given this error :
The model item passed into the dictionary is of type '<>f__AnonymousType1`1[System.Int32]', but this dictionary requires a model item of type 'SilkMeme.Models.Meme'.
I know it has something to do with the model being defined at the top of my view being different to the one I am sending the post request to but I'm not entirely sure how to fix it
View
#model SilkMeme.Models.Meme
....
#using (Html.BeginForm("Comment", "Memes", new { id = Model.SilkId }))
{
<label for="thought">Thoughts?</label>
<input type="text" name="thought"/>
<label for="rating">Rating?</label>
<input name="rating" type="range" min="0" max="10" step="1" />
<input type="submit" value="Post Thoughts" />
}
<div class="thoughts">
#foreach (var c in ViewBag.thoughts)
{
<p>- #c.ThoughtWords , #c.ThoughtRating / 10 meme</p>
}
</div>
Controller
public ActionResult Details(int? id)
{
var thoughts = from comment in db.Thoughts where comment.SilkId == id select comment;
ViewBag.thoughts = thoughts;
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Meme meme = db.Memes.Find(id);
if (meme == null)
{
return HttpNotFound();
}
return View(meme);
}
[HttpPost]
public ActionResult Comment(int id)
{
int thoughtid = (from m in db.Thoughts select m).OrderByDescending(e => e.ThoughtId).FirstOrDefault().ThoughtId + 1;
if (Request["thought"].ToString() != "")
{
Thought thought = new Thought()
{
ThoughtId = thoughtid,
SilkId = id,
Meme = db.Memes.Find(id),
ThoughtWords = Request["thought"],
ThoughtRating = Int32.Parse(Request["rating"])
};
db.Thoughts.Add(thought);
}
return View("Details", new { id = id });
}
This line.
return View("Details", new { id = id });
It is basically passing an anonymous object with Id property to your view which is strongly typed to Meme type and expects an object of Meme class.
If you save your data successfully, Ideally,you should do a redirect to the GET action (following PRG pattern)
[HttpPost]
public ActionResult Comment(int id)
{
int thoughtid = (from m in db.Thoughts select m)
.OrderByDescending(e => e.ThoughtId).FirstOrDefault().ThoughtId + 1;
if (Request["thought"].ToString() != "")
{
Thought thought = new Thought()
{
ThoughtId = thoughtid,
SilkId = id,
Meme = db.Memes.Find(id),
ThoughtWords = Request["thought"],
ThoughtRating = Int32.Parse(Request["rating"])
};
db.Thoughts.Add(thought);
db.SaveChanges();
}
return RedirectToAction("Details", new { Id=id });
}
Also, I recommend using MVC Modelbinding to read the submitted form data. You will find a ton of examples on stackoverflow to do that. When using ModelBinding, you can return the posted view model back to the view (with an error message if needed) and the ValidationSummary /ValidationMessgeFor helper methods can show an error message to user as needed.
I'm having some problems with my code and was hoping someone could give me a hand. Here's the snippet I'm working with:
[Authorize]
public ActionResult EventResults(int id)
{
List<Event> CompetitionEvents = Event.getEventsByCompetitionId(id);
ViewBag.CompetitionEvents = CompetitionEvents;
List<Person> Competitors = Competition.getCompetitorsByCompetitionID(id);
ViewBag.Competitors = Competitors;
List<Results> Results = Competition.getCompetitorResultsPairings(CompetitionEvents, Competitors);
ViewBag.Results = Results;
ViewBag.OrganizerEmail = Competition.getCompetitionById(id).OrganizerEmail;
return View();
}
#model BINC.Models.Results
#using BINC.Models;
#{
var eventList = ViewBag.CompetitionEvents as List<Event>;
var competitorList = ViewBag.Competitors as List<Person>;
var resultList = ViewBag.Results as List<Results>;
}
<h2></h2>
<p>Results:</p>
#using (Html.BeginForm())
{
foreach (var evt in eventList)
{
<fieldset>
<legend>#evt.activity.Name</legend>
<p>Event Description: #evt.activity.Description</p>
#foreach (var competitor in competitorList)
{
foreach (var result in resultList)
{
if (result.EventID == evt.id && result.CompetitorEmail == competitor.Email)
{
<p>Competitor: #competitor.FirstName #competitor.LastName</p>
<p>Score: #result.Score</p>
if (ViewBag.OrganizerEmail.Equals(#User.Identity.Name))
{
#Html.LabelFor(model => model.Score, "New Score ");
#Html.TextBoxFor(model => model.Score, new { maxlength = 10, style = "width:125px" })
<input type="submit" name="submitButton" value="Update" />
}
}
}
}
</fieldset>
}
}
[HttpPost]
public ActionResult EventResults(Results res)
{
//stuff
}
My problem is nothing other than the score is set on my Results object.
For example, when I put the value '15' into the text box and click 'Update', I'm passing the Result model object to the httppost method, which has everything set to null other than the 'score' field that I just entered.
Am I over complicating this? Is there an easier way?
I tried adding
#Html.HiddenFor(model => model.EventID);
#Html.HiddenFor(model => model.CompetitorEmail);
but that didn't seem to help any.
You are having multiple Submit buttons and that could be the issue, also this is not considered as good practise
<input type="submit" name="submitButton" value="Update" />
keep just one submit button at the end of the form
Basically-- make sure you pass the model to view-- and use the Html Helpers (ie TextBoxFor() and HiddenFor)
I don't think it's an issue with the submit button-- but the one thing that would probably help is to actually pass the model to the view. You are using the ViewBag to pass your data. Pass the model to View and your Html Helpers should generate the correct form names in order for the model binding to work.
I have a timesheet application that has a View where the user can select customers and tasks and add them to a dynamic table. This table is filled with the tasks and input fields for filling in hours worked.
For adding the new tasks in the dynamic table I use jQuery, so the savenewtask button is not a submit button. Instead I have a proper submit button for saving the hours when filled in.
The View is strongly typed to a model called TimesheetViewModel (see below). The controller passes the model to the View, and then the input fields are bound to properties in the model.
However, when I submit with the submit button and try to update the model in the Controller it doesn't update. It seemed from the Nerddinner tutorial (which I am using to learn MVC) that the model should automatically be updated using the values from the forms fields it had been bound to when you use UpdateModel(). But it doesn't. What am I doing wrong?
Here is all the relevant code:
View:
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<script src="../../Scripts/jquery-1.4.1.js" type="text/javascript"></script>
<script src="../../Scripts/jquery-1.4.1.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function () {
//Hook onto the MakeID list's onchange event
$("#CustomerId").change(function () {
//build the request url
var url = "Timesheet/CustomerTasks";
//fire off the request, passing it the id which is the MakeID's selected item value
$.getJSON(url, { id: $("#CustomerId").val() }, function (data) {
//Clear the Model list
$("#TaskId").empty();
//Foreach Model in the list, add a model option from the data returned
$.each(data, function (index, optionData) {
$("#TaskId").append("<option value='" + optionData.Id + "'>" + optionData.Name + "</option>");
});
});
}).change();
});
</script>
<h2>Index</h2>
<% using (Html.BeginForm())
{%>
<%: Html.ValidationSummary(true) %>
<fieldset>
<legend>Fields</legend>
<div>
<label for="Customers">
Kund:</label>
<%:Html.DropDownListFor(m => m.Customers, new SelectList(Model.Customers, "Id", "Name"), "Välj kund...", new { #id = "CustomerId" })%>
<label for="Tasks">
Aktiviteter:</label>
<select id="TaskId">
</select>
</div>
<p>
<input type="button" value="Save new task" id="savenewtask" />
</p>
<table width="100%">
<%--<% foreach (var task in Model.Tasks)--%>
<% foreach (var task in Model.WeekTasks)
{ %>
<tr>
<td>
<%: task.Customer.Name %>
</td>
<td>
<%: task.Name %>
</td>
<td>
<% foreach (var ts in task.TimeSegments)
{ %>
<input class="hourInput" type="text" size="2" id="<%: ts.Task.CustomerId + '_' + ts.TaskId + '_' + ts.Date %>"
value="<%: ts.Hours %>" />
<% } %>
</td>
</tr>
<% } %>
</table>
<input type="submit" value="Save hours" id="savehours" />
</fieldset>
<% } %>
</asp:Content>
From the Controller:
private TimesheetViewModel _model;
public TimesheetController()
{
_model = new TimesheetViewModel();
}
public ActionResult Index()
{
return View(_model);
}
[HttpPost]
public ActionResult Index(FormCollection collection)
{
try
{
UpdateModel(_model);
_model.Save();
return View(_model);
//return RedirectToAction("Index");
}
catch
{
return View();
}
}
The ViewModel:
public class TimesheetViewModel
{
private TimesheetContainer _model; //TimesheeContainer is an Entity Framework model
public TimesheetViewModel()
{
_model = new TimesheetContainer();
}
public IList<Customer> Customers
{ get { return _model.Customers.ToList(); } }
public IList<Task> Tasks
{ get { return _model.Tasks.ToList(); } }
public IList<Task> WeekTasks
{
get
{
//Get the time segments for the current week
DateTime firstDayOfWeek = DateTime.Parse("2010-12-05");
DateTime lastDayOfWeek = DateTime.Parse("2010-12-13");
List<TimeSegment> timeSegments = new List<TimeSegment>();
foreach (var timeSegment in _model.TimeSegments)
{
if(timeSegment.DateTimeDate > firstDayOfWeek && timeSegment.DateTimeDate < lastDayOfWeek)
timeSegments.Add(timeSegment);
}
//Group into tasks
var tasks = from timeSegment in timeSegments
group timeSegment by timeSegment.Task
into t
select new { Task = t.Key };
return tasks.Select(t => t.Task).ToList();
}
}
public IList<TimeSegment> TimeSegments
{ get { return _model.TimeSegments.ToList(); } }
public void Save()
{
_model.SaveChanges();
}
public void AddTimeSegments(Task task)
{
_model.AddToTasks(task);
_model.SaveChanges();
}
}
Partial class to get tasks for a specific week (only dummy week at this time for testing):
public partial class TimeSegment
{
public DateTime DateTimeDate
{ get { return DateTime.Parse(Date); } }
}
Why is the model not updating, and what can I change to make it work?
Put a breakpoint on your first ActionResult Index(), is that getting called when you do the submit? you may need [HttpGet] on it, otherwise I think it gets both.