in my ASP .Net MVC 2 application the user is shown a list of items and must click confirm before the list is persisted to the database.
When the user clicks confirm, in the HttpPost method the parameter is null. Everything works until the HttpPost method is called. At this point the list that must be persisted is null.
How do I get the confirmed values ?
I've tried using TempData in the HttpGet method but TempData is also null in the HttpPost method.
Here is the controller code.
public ActionResult Confirm()
{
List<ConfirmVehicleModel> vehicles = GetAllVehicles();
return View(vehicles);
}
[HttpPost]
public ActionResult Confirm(List<ConfirmVehicleModel> model)
{
//model is null, why ?
UploadVehiclesModelService service = new Models.UploadVehiclesModelService();
service.StoreVehicles(model, User.Identity.Name);
return RedirectToAction("Index", "UploadVehicles");
}
And here is the Confirm view:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<IEnumerable<RM.Application.Models.ConfirmVehicleModel>>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Confirm
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>
Confirm that the vehicles below should be added.</h2>
<table>
<tr>
<th>
ReferenceType
</th>
<th>
ReferenceName
</th>
</tr>
<% foreach (var item in Model)
{ %>
<tr>
<td>
<%= Html.Encode(item.ReferenceType) %>
</td>
<td>
<%= Html.Encode(item.ReferenceName) %>
</td>
</tr>
<% } %>
</table>
<div>
<% using (Html.BeginForm())
{ %>
<input type="submit" value="Confirm" />
|
<%= Html.ActionLink("Back to upload form", "Index") %>
<% } %>
</div>
</asp:Content>
Thanks for any help,
Kind regards
Bob
Your HTML.BeginForm() is out of place it should surround the values you wish to pass.
Try:
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>Confirm that the vehicles below should be added.</h2>
<% using (Html.BeginForm())
{ %>
<table>
<tr>
<th>
ReferenceType
</th>
<th>
ReferenceName
</th>
</tr>
<% foreach (var item in Model)
{ %>
<tr>
<td>
<%= Html.Encode(item.ReferenceType) %>
<%= Html.HiddenFor(model => item.ReferenceType)%>
</td>
<td>
<%= Html.Encode(item.ReferenceName) %>
<%= Html.HiddenFor(model => item.ReferenceName)%>
</td>
</tr>
<% } %>
</table>
<div>
<input type="submit" value="Confirm" />
|
<%= Html.ActionLink("Back to upload form", "Index") %>
<% } %>
</div>
Nicholas advice helped. I replaced the foreach loop with a for loop and used Html.HiddenFor.
<% for (int i = 0; i < Model.Count(); i++)
{ %>
<%= Html.HiddenFor(model=>model[i].ReferenceType) %>
<%= Html.HiddenFor(model=>model[i].ReferenceName) %>
<tr>
<td>
<%= Html.Encode(Model[i].ReferenceType) %>
</td>
<td>
<%= Html.Encode(Model[i].ReferenceName) %>
</td>
</tr>
<% } %>
I also changed the top line of the the view to (previously IEnumerable, now List).
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<List<RM.Application.Models.ConfirmVehicleModel>>" %>\\
The List<ConfirmVehicleModel> model parameter of the HttpPost method is now populated.
Related
I'm creating multiple tables by date and I want my html table rows to be a different colour depending on a number of conditions. With the help of this question I tried out the following in my shift view:
<% (Date.current - 6.months..Date.current + 6.months).each do |date|%>
<% if !Shift.where(date: date).blank? %>
<% #shifts = LaunchShift.where(date: date).all.order(start_time: :asc, booked: :desc) %>
<h2><%= date.strftime('%e %b %Y') %></h2>
<div class="table-responsive">
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>...</th>
</tr>
</thead>
<% #shifts.each_with_index do |shift, index| %>
<% if shift.booked? %>
<% #booker = Member.find(shift.booked_by) %>
<% end %>
<tbody>
<% if shift.booked? %> <---Trying this for changing colour
<tr style="background-color:red">
<% else %>
<tr>
<% end %>
<td><%= shift.start_time.strftime("%H:%M") %></td>
<% if shift.booked? %>
<td>Booked</td>
<td><%= link_to #booker.name, member_path(#booker) %></td>
<% else %>
<td>Free</td>
<td></td>
<% end %>
[...]
</tr>
</tbody>
<% end %>
</table>
</div>
<% end %>
But only some of the rows marked as booked are red, though the shifts.booked? returns as True. Does this approach work differently than I think it does? Is there a better way to do this that that doesn't use JS/JQuery (Don't know those). Any advice?
Thank you!
You can try like
.booked{
background-color: red;
}
.available{
background-color: grey;
}
<tr class="<%= shift.booked? ? 'booked' : 'available' %>">
</tr>
If shift is booked then booked class will apply otherwise available class will apply using ternary operator.
#ashvin's solution is fine but it would be nice to go one step farther and put the ternary operator into a helper method, thereby keeping your view free of logic. e.g.
<tr class="<%= booked_class(shift) %>">
create a shift_helper.rb file in the helpers folder
module ShiftHelper
def booked_class(shift)
shift.booked? ? "booked" : "available"
end
end
HomeController.cs
[HttpPost]
public ActionResult Index(int searchtext)
{
var data = (from pm in db.ProductMasters
join shi in db.SuppliersHotelsInfoes
on pm.ProductID equals shi.LocHotelID
where shi.SearchID == searchtext
select new {pm.ProductId,pm.Image,shi.HotelName,shi.HotelPrice}).ToList().Take(10);
ViewBag.Data = data;
return View();
}
Transfering the data to the view
<% try { %>
<tr><td>
<% foreach(var item in ViewBag.Data){ %>
<img alt="" src="<%= item. %>" />
</td><%} %> <% foreach(var item in ViewBag.Data) { %>
<td><%: Html.DisplayFor(modelItem => "")%></td>
<td><%: Html.DisplayFor(modelItem => "")%> </td>
<td><%: Html.DisplayFor(modelItem => "")%></td>
</tr>
<%} }%>
<%catch(Exception ex){ }%>
Here, how to get the values in "var item"
You really should use a ViewModel, I can't remember last time I used the ViewBag
I'm also going to use Razor here, its syntax is much more terse. You don't need to use DisplayFor if you're just presenting the data:
#foreach(var item in ViewBag.Data){
<tr>
<td><img alt="" src="#item.Image" /><td>
<td>#item.ProductId</td>
<td>#item.HotelName</td>
<td>#item.HotelPrice</td>
</tr>
}
I have a view that is strongly typed:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MPKwithMVC.Models.SmartFormViewModel>" %>
Works great to generate the view, but when I post, I have an ActionResult defined:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Next(MPKwithMVC.Models.SmartFormViewModel model)
{ .. }
Which I would imagine get hit when my next button is clicked (it works if I change the argument to a FormsCollection). I instead get a message saying "No parameterless constructor defined for this object".
What am I doing wrong?
My SmartFormsViewModel is:
[Serializable]
public class SmartFormViewModel
{
public List<Question> Questions { get; set; }
public List<Answer> Answers { get; set; }
public SmartFormViewModel(List<Question> questions, List<Answer> answers)
{
this.Questions = questions;
this.Answers = answers;
}
public SmartFormViewModel()
{
}
}
And here is the View:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MPKwithMVC.Models.SmartFormViewModel>" %>
<%# Import Namespace="MPKwithMVC.Models" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
SmartForms
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>
Questionaire</h2>
<% using (Html.BeginForm("Next", "SmartForms"))
{ %>
<div style="float: left; margin-right: 2em;">
<% Html.RenderPartial("NavigationPanel", Model); %>
</div>
<div>
<table>
<%
foreach (Question question in (Model.Questions))
{ %>
<tr>
<td>
<div style="text-align: right; width: 20em;">
<%= Html.Encode(question.QuestionText)%>
</div>
</td>
<td>
<div style="float: left;">
<% if (question.QuestionType == 1)
{ %>
<%= Html.TextBoxFor(model => model.Answers[(int)question.QuestionID - 1].AnswerValue) %>
<% } %>
<% if (question.QuestionType == 2)
{ %>
<%= Html.RadioButton("yn" + question.QuestionID, "Yes", false)%>Yes
<%= Html.RadioButton("yn" + question.QuestionID, "No", true)%>No
<% } %>
</div>
<% if (question.Required == true)
{ %>
<div style="color: Red; float: right; margin-left: 3px;">
*</div>
<% } %>
</td>
</tr>
<%
} %>
<tr>
<td>
<%
if (ViewData["errorMsg"] != null)
{%>
<div style="color:Red;">
<%= Html.Encode(ViewData["errorMsg"].ToString()) %>
</div>
<% } %>
</td>
<td>
<div style="margin-top: 1em;">
<button name="button" value="next">Next</button>
</div>
</td>
</tr>
</table>
</div>
<% } %>
</asp:Content>
Have a look at the input names that are being generated within your HTML. I think you have an issue with the naming of your controls thus the default model binding is failing since you mentioned that the using FormCollection works correctly. I am making this assumption since I don't know what your Questions and Answers classes look like
<%= Html.TextBoxFor(model =>
model.Answers[(int)question.QuestionID - 1].AnswerValue) %>
Won't this render something similar to the following; which if I'm not mistaken will not bind to your model. The same applies for the RadioButtons.
<input type="text" name="Answers[0].AnswerValue" id="Answers_0__AnswerValue" value="somevalues"/>
The RadioButton helper should be
<%= Html.RadioButton("Questions[" + question.QuestionID + "].ID", "Yes", false)%> // you now get a list of questions
<input type="radio" name="Questions[1].ID" id="Questions_1__ID" value="No" checked="checked"/>
There are some ways you can try to resolve this:
Correctly name your input controls so that they match you ViewModel. (This includes posting back all the required fields for you model - I think that that default values are used when the model binding occurs if its not posted)
Create a custom model binder
You may need to tell the Binder what the Prefix of the input fields are. ([Bind] attribute) to specifically include or exclude form fields.
Create a new Model containing the values you expect to post back
I think that overall that your approach needs to slightly change. From the info provided, your Q&As are closely related. Depending on the question type your 'answer' is either boolean or freetext. At the moment your are not posting a List<Questions> back to the server. Answers, yes, but they are not I don't think that it is recognised as List<Answers>.
Haacked has a post which I think is related to your issue and this SO question further indicates that it may still applly to ASP-MVC-2.
As the error message suggest, your SmartFormViewModel class needs to contain a parameterless constructor.
This is a great example of what you would like to do
I would also suggest you use the strongly typed helpers from Html extensions to generate your form fields.
e.g.
Html.HiddenFor(x => x.SomeField)
Html.TextBoxFor(x => x.SomeEditableField)
Make sure that your <form>'s action points to your Next method.
I'm using ASP.NET MVC 2 and am struggling with saving child entities. I have an existing Invoice entity (which I create on a separate form) and then I have a LogHours view that I'd like to use to save InvoiceLog's, which are child entities of Invoice. Here's the view:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<TothSolutions.Data.Invoice>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Log Hours
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="HeadContent" runat="server">
<script type="text/javascript">
$(document).ready(function () {
$("#InvoiceLogs_0__Description").focus();
});
</script>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>Log Hours</h2>
<% using (Html.BeginForm("SaveHours", "Invoices")) {%>
<%: Html.ValidationSummary(true) %>
<fieldset>
<legend>Fields</legend>
<table>
<tr>
<th>Date</th>
<th>Description</th>
<th>Hours</th>
</tr>
<%
int index = 0;
foreach (var log in Model.InvoiceLogs) {
%>
<tr>
<td><%: log.LogDate.ToShortDateString() %></td>
<td><%: Html.TextBox("InvoiceLogs[" + index + "].Description")%></td>
<td><%: Html.TextBox("InvoiceLogs[" + index + "].Hours")%></td>
<td>Hours</td>
</tr>
<%
index++;
}
%>
</table>
<p>
<%: Html.Hidden("InvoiceID") %>
<%: Html.Hidden("CreateDate") %>
<input type="submit" value="Save" />
</p>
</fieldset>
<% } %>
<div>
<%: Html.ActionLink("Back to List", "Index") %>
</div>
</asp:Content>
And here's the controller code:
//GET: /Secure/Invoices/LogHours/
public ActionResult LogHours(int id)
{
var invoice = DataContext.InvoiceData.Get(id);
if (invoice == null)
{
throw new Exception("Invoice not found with id: " + id);
}
return View(invoice);
}
//POST: /Secure/Invoices/SaveHours/
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult SaveHours([Bind(Exclude = "InvoiceLogs")]Invoice invoice)
{
TryUpdateModel(invoice.InvoiceLogs, "InvoiceLogs");
invoice.UpdateDate = DateTime.Now;
invoice.DeveloperID = DeveloperID;
//attaching existing invoice.
DataContext.InvoiceData.Attach(invoice);
//save changes.
DataContext.SaveChanges();
//redirect to invoice list.
return RedirectToAction("Index");
}
And the data access code:
public static void Attach(Invoice invoice)
{
var i = new Invoice { InvoiceID = invoice.InvoiceID };
db.Invoices.Attach(i);
db.Invoices.ApplyCurrentValues(invoice);
}
In the SaveHours action, it properly sets the values of the InvoiceLog entities after I call TryUpdateModel but when it does SaveChanges it doesn't update the database with the new values. Also, if you manually update the values of the InvoiceLog entries in the database and then go to this page it doesn't populate the textboxes so it's clearly not binding correctly.
Thanks,
Justin
Got it working, I needed to populate the InvoiceLogID and InvoiceID in hidden fields so they'd get populated in the EntityCollection.
Calling Index view is giving me this very very annoying error . Can anybody tell me what to do about it
Error:
The model item passed into the dictionary is of type 'System.Collections.Generic.List1[MvcApplication13.Models.Groups]', but this dictionary requires a model item of type 'MvcApplication13.Helpers.PaginatedList1[MvcApplication13.Models.Groups]'.
public ActionResult Index(int? page)
{
const int pageSize = 10;
var group =from p in _db.Groups orderby p.int_GroupId select p;
var paginatedGroup = group.Skip((page ?? 0) * pageSize).Take(pageSize).ToList();
return View(paginatedGroup);
}
View:
>" %>
Index
<h2>Index</h2>
<table>
<tr>
<th></th>
<th>
int_GroupId
</th>
<th>
vcr_GroupName
</th>
<th>
txt_GroupDescription
</th>
<th>
bit_Is_Deletable
</th>
<th>
bit_Active
</th>
<th>
int_CreatedBy
</th>
<th>
dtm_CreatedDate
</th>
<th>
int_ModifiedBy
</th>
<th>
dtm_ModifiedDate
</th>
</tr>
<% foreach (var item in Model) { %>
<tr>
<td>
<%= Html.ActionLink("Edit", "Edit", new { id=item.int_GroupId }) %> |
<%= Html.ActionLink("Details", "Details", new { id=item.int_GroupId })%> |
<%= Html.ActionLink("Delete", "Delete", new { id=item.int_GroupId })%>
</td>
<td>
<%= Html.Encode(item.int_GroupId) %>
</td>
<td>
<%= Html.Encode(item.vcr_GroupName) %>
</td>
<td>
<%= Html.Encode(item.txt_GroupDescription) %>
</td>
<td>
<%= Html.Encode(item.bit_Is_Deletable) %>
</td>
<td>
<%= Html.Encode(item.bit_Active) %>
</td>
<td>
<%= Html.Encode(item.int_CreatedBy) %>
</td>
<td>
<%= Html.Encode(String.Format("{0:g}", item.dtm_CreatedDate)) %>
</td>
<td>
<%= Html.Encode(item.int_ModifiedBy) %>
</td>
<td>
<%= Html.Encode(String.Format("{0:g}", item.dtm_ModifiedDate)) %>
</td>
</tr>
<% } %>
</table>
>>", "UpcomingDinners", new { page = (Model.PageIndex + 1) }) %>
<p>
<%= Html.ActionLink("Create New", "Create") %>
</p>
You lost some data when you pasted your code. This is what I think you meant to paste:
View
<%# Page Title="" Language="C#"
MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage<MvcApplication13.Helpers.PaginatedList<MvcApplication13.Models.Groups>>" %>
If so , you code should look similar to this (ie. create a new instance of the PaginatedList class):
public ActionResult Index(int? page) {
const int pageSize = 10;
var upcomingDinners = dinnerRepository.FindUpcomingDinners();
var paginatedDinners = new PaginatedList<Dinner>(upcomingDinners, page ?? 0, pageSize);
return View(paginatedDinners);
}
http://nerddinnerbook.s3.amazonaws.com/Part8.htm