New new to C# and MVC, so apologies in advance for posting something which is probably obvious.I have looked at similar answers but still can't see how and what value in the RadioButtonFor should be used so that it can be POSTed back to the controller.
Controller
[HttpPost]
public ActionResult Score(ExamViewModel exam)
{
const int AddCorrect = 1;
var correct = from c in db.Answers
where c.ID == 1
select c.CorrectAnswer;
if (ModelState.IsValid)
{
if (correct == exam.CorrectAnswer)
{
ViewData["message"] = "Correct Answer!";
return View("Results");
}
else
{
var feedback = from g in db.Questions
where g.ID == 1
select g.GrammarPoint;
ViewData["message"] = "That's not the right answer.";
ViewData["feedback"] = feedback;
return View("Results");
}
}
return View("Results");
And the View
#model AccessEsol.Models.ExamViewModel
#{
ViewBag.Title = "TakeTest";
}
<h2>TakeTest</h2>
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<div class="display-label">
<h3>Click the correct answer:</h3>
</div>
<div class="display-field">
<strong>#Html.DisplayFor(model => model.Text.Text )</strong>
</div>
#Html.DisplayFor(model => model.Foil1.Foil1)
#Html.RadioButtonFor(model =>model.Foil1, "Incorrect" )
#Html.DisplayFor(model => model.Foil2.Foil2)
#Html.RadioButtonFor(model => model.Foil2, "Incorrect" )
#Html.DisplayFor(model => model.Foil3.Foil3)
#Html.RadioButtonFor(model => model.Foil3, "Incorrect")
#Html.DisplayFor(model => model.CorrectAnswer.CorrectAnswer)
#Html.RadioButtonFor(model => model.CorrectAnswer, "Correct")
<p>
<input type="submit" value="Submit Answers" />
</p>
</fieldset>
}
I also tried to pass a string from the CorrectAnswer into the Score Controller without success.Much appreciated if you can point to how can checked RadioButton value can be passed back to the Controller?
You should not have 3 different properties but instead a single one that will contain the answer. This will allow you to group the radio buttons and be able to select only one of them:
#model AccessEsol.Models.ExamViewModel
#{
ViewBag.Title = "TakeTest";
}
<h2>TakeTest</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<div class="display-label">
<h3>Click the correct answer:</h3>
</div>
<div class="display-field">
<strong>#Html.DisplayFor(model => model.Text.Text)</strong>
</div>
#Html.DisplayFor(model => model.Foil1.Foil1)
#Html.RadioButtonFor(model => model.Answer, "1")
#Html.DisplayFor(model => model.Foil2.Foil2)
#Html.RadioButtonFor(model => model.Answer, "2")
#Html.DisplayFor(model => model.Foil3.Foil3)
#Html.RadioButtonFor(model => model.Answer, "3")
#Html.DisplayFor(model => model.CorrectAnswer.CorrectAnswer)
#Html.RadioButtonFor(model => model.Answer, "4")
<p>
<input type="submit" value="Submit Answers" />
</p>
</fieldset>
}
and then in your controller action check if the Answer property value that is sent is the correct one for this question. As you can see from the view we have multiple answer for the question and the value is what will get sent to the server. Usually you will use the ID of the answer as value and on the server you can compare whether this is the correct answer for the question.
Related
i have problem creating view with multiple forms with in same view where i have get action for data view and post action for data collection.
Here is my main view:
#model Models.BuildingNewBuildingViewModel
#{
ViewBag.Title = "Index";
}
#if (TempData["Added"] != null)
{
<div class="alert alert-success alert-dismissible fade in" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
#TempData["Added"]
</div>
}
#if (TempData["AddError"] != null)
{
<div class="alert alert-danger alert-dismissible fade in" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
#TempData["AddError"]
</div>
}
<h1>Svěřené budovy pro: #ViewBag.Name</h1>
<table class="table-bordered table-responsive table table-condensed">
<thead>
<tr>
<th>Id budovy</th>
<th>Podlaží</th>
<th>Použití</th>
<th>Výměra</th>
<th>Datum</th>
<th>Administrace</th>
</tr>
</thead>
<tbody>
#for (int i = 0; i < Model.NewBuildingViewModels.Count; i++)
{
using (Html.BeginForm("Index", "Home", FormMethod.Post, new { #class = "form-horizontal", id = "Form" }))
{
Html.RenderPartial("IndexPartial", Model.NewBuildingViewModels[i]);
}
}
</tbody>
</table>
Here is form:
#model Models.NewBuildingViewModel
#Html.HiddenFor(x => x.Building, Model.Building)
<tr>
<td>#Model.Building</td>
<td>
#if (Model.Floor > 0)
{
#Model.Floor
#Html.HiddenFor(x => x.Floor)
}
else
{
#Html.TextBoxFor(x => x.Floor, new {#Value = ""})
#Html.ValidationMessageFor(x => x.Floor)
}
</td>
<td>
#if (Model.Usage != null)
{
#Model.Usage
#Html.HiddenFor(x => x.Usage)
}
else
{
#Html.TextBoxFor(x => x.Usage)
#Html.ValidationMessageFor(x => x.Usage)
}
</td>
<td>
#if (Model.Size > 0)
{
#Model.Size
#Html.HiddenFor(x => x.Size)
}
else
{
#Html.TextBoxFor(x => x.Size, new {#Value = ""})
#Html.ValidationMessageFor(x => x.Size)
}
</td>
<td>
#if (Model.Date != DateTime.MinValue)
{
#Model.Date.Date
#Html.HiddenFor(x => x.Date)
}
else
{
#Html.TextBoxFor(x => x.Date)
#Html.ValidationMessageFor(x => x.Date)
}
</td>
<td>
#if (!Model.NewBuildingDataInDatabase)
{
<button type="submit">Odeslat</button>
}
else
{
if (Model.MarksInDatabase)
{
<i class="fa fa-check checkIcon" aria-hidden="true"></i>
}
else
{
Zadat Hodnocení
}
}
</td>
</tr>
}
And my problem is when i send non valid data to controller. Validation message shows for all forms. I want to see all forms but validation messages only for form which sended data.
Here is my action:
[HttpPost]
public ActionResult Index(NewBuildingViewModel buildingNewBuildingViewModel)
{
BuildingForIcoCommand buildingForIcoCommand = new BuildingForIcoCommand();
var buildingsForIco = buildingForIcoCommand.GetBuildingListForIco(AppContext);
if (ModelState.IsValid)
{
ViewBag.Name = User.Identity.Name;
ViewModelBuilder viewModelBuilder = new ViewModelBuilder();
DaoBase<NewBuilding, int> daoBase = new DaoBase<NewBuilding, int>(AppContext.NhSession);
daoBase.Save(viewModelBuilder.CreateNewBuilding(buildingNewBuildingViewModel));
return View(buildingForIcoCommand.GetBuildingListForIco(AppContext));
}
return View("Index",buildingsForIco);
}
All of your form fields will have the name and id attributes because you are rendering them in a partial. This is why they are all flagged as invalid.
Any good solution will probably involve ajax.
One approach to revolving this would be to use a single edit form. Each rendered item would have an edit button, clicking this button would populate the form with the item's data via javascript. This could be a modal or similar. This approach works well with jQuery / unobtrusive validation.
Another approach would be to return a custom response from your controller eg status 400 and the invalid fields derived from the model state. You can then show a suitable error message in the right field.
I am not sure why am i getting sequence contains no elements im kinda new mvc
My Controller
[HttpPost]
public ActionResult DeleteAnnounce(string announce)
{
using (var db = new HarmonyMainServerDbContext())
{
ANNOUNCEMENT annoude = db.Announcer.First(c => c.AnnounceTitle == announce);
db.Announcer.Remove(annoude);
db.SaveChanges();
}
return RedirectToAction("ViewAnnounce");
}
My View
<div id="clientDetailsContainer">
#using (Html.BeginForm("Announcements", "Admin", FormMethod.Post, new { id = "Announcers" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<div class="module-controls" style="display:block;">
<img src="../Images/src/back.png"/><span>Back to List</span>
<a class="k-button module-action" id="submitSave" title="haha"><img src="../Images/src/subgrid_save.png"/><span>Save</span></a>
<a class="k-button module-action" id="submitDelete" ><img src="../Images/src/subgrid_save.png"/><span>Delete</span></a>
</div>
<h2>Announcement</h2>
#Html.HiddenFor(model => model.AnnounceID)
<div class="client-details" style="margin-left: 50px">
<p>
<label class="label-ant">Announcement Title</label>
#Html.TextBoxFor(model => model.AnnounceTitle, new {#class="k-textbox", #style="width:250px" })
#Html.ValidationMessageFor(model => model.AnnounceTitle)
</p>
<p>
<label class="label-ant">Announcement Remarks</label>
#Html.TextBoxFor(model => model.AnnounceRemarks, new {#class="k-textbox", #style="width:250px" })
#Html.ValidationMessageFor(model => model.AnnounceRemarks)
</p>
<p>
<label class="label-ant">Announce Link</label>
#Html.TextBoxFor(model => model.AnnounceLink, new {#class="k-textbox", #style="width:250px" })
#Html.ValidationMessageFor(model => model.AnnounceLink)
</p>
</div>
}
<script>
$("#submitSave").click(function () {
$("#Announcers").submit();
});
$("#submitDelete").click(function () {
$("#Announcers").attr({
"action" : "/Admin/DeleteAnnounce",
});
});
</script>
</div>
In collection there is few methods which you need to understand difference
First - means that if nothing is returned it will throw exception,
but it allows collection to have more than first
Single - means get only one item, so if collection match more than
one or non you will get exception, this is kind "constraint"
FirstOrDefault - means that collection can have more than one or none items matching
SingleOrDefault - collection should have only one or non items matching items
It's because you're using First(). This throws the exception if there's no Announcer with matching title.
See https://msdn.microsoft.com/library/bb291976%28v=vs.100%29.aspx
Within my project I am trying to change the users ability from only being able to "Edit" an address field to being able to "Create" and "Edit" if it already exist within the same view. I receive the following server error Click Here.
I then ran it through the debugger and received the following result Click Here Too!. I can see that my User ID is being passed as null in the controller which creates the server error. But after following Mr. Pratt advice I can see the User Id is being passed into the View I am unsure why this is happening or where I've made the mistake.
Controller
// GET: /UserProfile/Edit/5
public ActionResult Edit(int id)
{
var userProfile = db.UserProfiles.FirstOrDefault(up => up.UserId == id);
var query = from o in db.UserProfiles
where o.Address != null
select o;
ViewBag.Query = query;
// if the user is not an admin
if (!User.IsInRole("admin"))
{
// look up current user id
var currentUserId = (int)System.Web.Security.Membership.GetUser().ProviderUserKey;
// check to make sure that the user profile is attached to the currently logged in user
if (userProfile.UserId == currentUserId)
{
return View(userProfile);
}
else
{
throw new Exception("you cannot access a user profile that is not your own");
}
}
// the user is an admin, so let them view the profile
return View(userProfile);
}
View
#model YardLad.Models.Domain.UserProfile
#{
ViewBag.Title = "Edit Profile";
}
<h2>Edit Profile</h2>
#if (User.IsInRole("contractor") || User.IsInRole("contractor2"))
{
<style>
#content a {
color: cornflowerblue;
}
#content a:hover {
color: cornflowerblue;
}
</style>
}
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
#Html.HiddenFor(m => m.UserId)
#Html.HiddenFor(m => m.AcceptSMS)
#Html.HiddenFor(m => m.IsActive)
#Html.HiddenFor(m => m.LastLoginDate)
#Html.HiddenFor(m => m.AddressId)
<div class="editor-label">
Name
</div>
<div class="editor-field">
#Html.EditorFor(m => m.FirstName)
#Html.ValidationMessageFor(m => m.FirstName)
</div>
<div class="editor-field">
#Html.EditorFor(m => m.LastName)
#Html.ValidationMessageFor(m => m.LastName)
</div>
<div class="editor-label">
#Html.LabelFor(m => m.Phone)
</div>
<div class="editor-field">
#Html.TextBoxFor(m => m.Phone)
#Html.ValidationMessageFor(m => m.Phone)
</div>
<div class="editor-label">
#Html.LabelFor(m => m.Mobile)
</div>
<div class="editor-field">
#Html.TextBoxFor(m => m.Mobile)
#Html.ValidationMessageFor(m => m.Mobile)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.DateOfBirth)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.DateOfBirth)
#Html.ValidationMessageFor(model => model.DateOfBirth)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Gender)
</div>
<div class="editor-field">
#Html.RadioButton("Gender", "male") male
#Html.RadioButton("Gender", "female") female
#Html.ValidationMessageFor(m => m.Gender)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.AddressId, "Address")
</div>
var address = ViewContext.ViewData.ModelState["Address"];
if (address != null && address.Errors.Count() > 0)
{
#Html.ActionLink("Add a billing address", "Create", "Address",
new { returnUrl = "UserProfile/Edit", userId = Model.UserId }, null)
}
else
{
<div class="editor-field">
#Html.DisplayFor(m => m.Address.Line1)<br />
#*#if (Model.Address.Line2 != null && address.Any())
{
#Html.DisplayFor(m => m.Address.Line2)<br />
}*#
#Html.DisplayFor(m => m.Address.City), #Html.DisplayFor(m => m.Address.State.Abbreviation) #Html.DisplayFor(m => m.Address.PostalCode)
</div>
if (ViewBag.Query != null || ViewBag.Query != "" )
{
#Html.ActionLink("Add an address", "Create", "Address", new { id = Model.AddressId, returnUrl = "UserProfile/Edit", userId = Model.UserId }, null)
}
else
{
#Html.ActionLink("Edit address", "Create", "Address", new { id = Model.AddressId, returnUrl = "UserProfile/Edit", userId = Model.UserId }, null)
}
}
#*<div class="editor-field">
<span>#Html.CheckBoxFor(m => m.AcceptSMS)</span>
<span class="editor-label">
#Html.LabelFor(m => m.AcceptSMS, "Accept SMS Service")
</span>
<p style="width: 50%; min-width: 300px;">
this free service allows you to send and receive text updates for easier access to making payments, scheduling reoccurring services, rating services, etc.<br />
<span style="font-size: .9em; color: forestgreen;">standard SMS charges still apply (based on your mobile carrier and plan)</span>
</p>
</div>*#
<p>
<input type="submit" value="Save" />
</p>
}
<div>
#Html.ActionLink("Back to My Account", "MyAccount", "Account")
</div>
userProfile is null. You need to do proper null-checking:
var userProfile = db.UserProfiles.FirstOrDefault(up => up.UserId == id);
if (userProfile == null)
{
return new HttpNotFoundResult();
}
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.
In my MedicalProductController, I am trying to make my Edit action able to edit multiple objects on one page. To do that, I plan on my HTTPPOST edit action method receiving an IEnumerable<MedicalProduct> instead of the MedicalProduct that the scaffolding set up for me.
When I click save to submit some changes, I get an ArguementNullException unhandled on the line: _db.Entry(productList).State = EntityState.Modified; and I don't understand why it is null.
MedicalProductController:
public class MedicalProductController : Controller
{
private MvcMedicalStoreDb _db = new MvcMedicalStoreDb();
// some code omitted for brevity
public ActionResult Edit(int id = 0)
{
MedicalProduct product = _db.Products.Find(id);
if (product == null)
{
return HttpNotFound();
}
var productList = new List<MedicalProduct> { product };
var viewModel = GetMedicalProductViewModelList(productList);
return View(viewModel);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(IEnumerable<MedicalProduct> productList)
{
if (ModelState.IsValid)
{
_db.Entry(productList).State = EntityState.Modified;
_db.SaveChanges();
return RedirectToAction("Index");
}
//var productList = new List<MedicalProduct> { product };
var viewModel = GetMedicalProductViewModelList(productList);
return View(viewModel);
}
}
Edit.cshtml:
#model IEnumerable<MvcMedicalStore.Models.MedicalProductViewModel>
#{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>MedicalProduct</legend>
#foreach (var modelItem in Model)
{
#Html.HiddenFor(item => modelItem.ID)
<div class="editor-label">
#Html.LabelFor(item => modelItem.Name)
</div>
<div class="editor-field">
#Html.EditorFor(item => modelItem.Name)
#Html.ValidationMessageFor(item => modelItem.Name)
</div>
<div class="editor-label">
#Html.LabelFor(item => modelItem.Price)
</div>
<div class="editor-field">
#Html.EditorFor(item => modelItem.Price)
#Html.ValidationMessageFor(item => modelItem.Price)
</div>
<div class="editor-label">
#Html.LabelFor(item => modelItem.BrandName)
</div>
<div class="editor-field">
#Html.DropDownListFor(item => modelItem.BrandName, modelItem.BrandSelectListItem)
#Html.ValidationMessageFor(item => modelItem.BrandName)
</div>
}
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
It looks to me like the model binder isn't able to bind to your collection, which would cause it to be null. The reason it's doing that is because you're not specifying an index for each of the elements. That means MVC has no way to determine how to bind them correctly.
Edit
I've figured out why the last revision of this answer didn't work. Firstly, IEnumerable<T> doesn't have a direct indexer. Instead you would use Model.ElementAt(i).ID to access the ID property. However, this actually wouldn't solve the problem with the model binding issue as, for some reason, this doesn't generate the proper indices on the name attributes for the generated <input> fields. (More on this below.)
There are two ways to fix that. The first way would be to pass a List to the view, instead of IEnumerable, then accessing the fields as I showed earlier. However, the better way would be to create an EditorTemplate instead. This will be easier because it saves you having to change your existing methods which are generating your view model. So you'll need to follow these steps:
Create an EditorTemplates folder inside your view's current folder (e.g. if your view is Home\Index.cshtml, create the folder Home\EditorTemplates).
Create a strongly-typed view in that directory with the name that matches your model (e.g in this case the view would be called MedicalProductViewModel).
Move the bulk of your original view into that new template.
You'll end up with the following:
#model MedicalProductViewModel
#Html.HiddenFor(item => Model.ID)
<div class="editor-label">
#Html.LabelFor(item => Model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(item => Model.Name)
#Html.ValidationMessageFor(item => Model.Name)
</div>
<div class="editor-label">
#Html.LabelFor(item => Model.Price)
</div>
<div class="editor-field">
#Html.EditorFor(item => Model.Price)
#Html.ValidationMessageFor(item => Model.Price)
</div>
<div class="editor-label">
#Html.LabelFor(item => Model.BrandName)
</div>
<div class="editor-field">
#Html.DropDownListFor(item => Model.BrandName, Model.BrandSelectListItem)
#Html.ValidationMessageFor(item => Model.BrandName)
</div>
Notice how we're no longer using any indexing notation to access the model properties.
Now in your Edit.cshtml view, you'd be left with this:
#model IEnumerable<MvcMedicalStore.Models.MedicalProductViewModel>
#{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>MedicalProduct</legend>
#Html.EditorFor(m => m)
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Although I gave a brief explanation at the start, I should really explain what this is actually doing. Your original HTML would have produced output like the following:
<input name="ID" type="text" value="1" />
<input name="Name" type="text" value="Name 1" />
<input name="ID" type="text" value="2" />
<input name="Name" type="text" value="Name 2" />
As you can see, multiple input fields share the same name. That's why the model binder is tripping up, because your action is telling it to bind to a collection and the binder needs to be able to distinguish between each element in the collection. EditorTemplates are smart enough to figure out when you're working with a collection and will apply indices to your input fields automatically. What the code above will do is generate output like this instead:
<input name="[0].ID" type="text" value="1" />
<input name="[0].Name" type="text" value="Name 1" />
<input name="[1].ID" type="text" value="2" />
<input name="[1].Name" type="text" value="Name 2" />
As you can see, the fields now have an index associated with them. That gives the model binder all the information it needs to be able to add all of the items to the collection. Now that's out of the way, we can get back to fixing your product saving code.
What Gert said is still right about the way you're trying to save productList. You need to be setting the EntityState.Modified flag on each individual item in that collection:
if (ModelState.IsValid)
{
foreach (var product in productList)
_db.Entry(product).State = EntityState.Modified;
_db.SaveChanges();
return RedirectToAction("Index");
}
See if that works.