My problem is that strongly typed data passed from my Controller to my view comes out empty (all it's properties are null).
I would also like to bind selected value in radiobuttons (marked as QUESTION2) to model property "GivenAnwser" but it doesn't seem to work either.
Type passed around is a ViewModel
public class HomeController : Controller
{
[HttpGet]
public ActionResult Index()
{
QuestionViewModel question = Manager.GetQuestion();
return View(question);
}
[HttpPost]
public ActionResult Index(QuestionViewModel question,Anwser givenAnwser)
{
//QuestionViewModel is returned but all it's properties are null.
return View(question);
}
}
VIEW
#model Quiz.ViewModels.QuestionViewModel
#{
ViewBag.Title = "Home Page";
}
#Html.Label("Question:")
#if (Model.CorrectAnwser != null)
{
//some code
}
#Html.DisplayFor(model => model.Question.Text)
//I have tried with Hidden fields and without them
#Html.HiddenFor(model => model.Question)
#Html.HiddenFor(model => model.Anwsers)
#using (Html.BeginForm("Index", "Home"))
{
foreach (var anwser in Model.Anwsers)
{
//QUESTION 2
<input type="radio" name="givenAnwser" value="#anwser" />
<label>#anwser.Text</label>
<br />
}
<input type="submit" value="Check!" />
}
QuestionViewModel
public class QuestionViewModel
{
public QuestionViewModel()
{
this.Anwsers = new List<Anwser>();
}
public Question Question { get; set; }
public List<Anwser> Anwsers { get; set; }
public Anwser GivenAnwser { get; set; }
public bool CorrectAnwser { get; set; }
}
EDIT:
ModelState contains an error:
"The parameter conversion from type 'System.String' to type 'Quiz.Models.Anwser' failed because no type converter can convert between these types."
<input type="radio" name="givenAnwser" value="#anwser" />
this line is setting a complex type "#answer" as the value of the radio button.
this might just do a ToString() of the type during rendering.
When you post it back, MVC might be trying to convert this string value back to
Quiz.Models.Anwser
and failing.
you should probably render
<input type="radio" name="givenAnwser" value="#anwser.SomeBooleanValue" />
p.s. also, why not use the Html Extension to render the radio button.
You can't bind a complex type (Question) to a Hidden field. You would need to bind to the separate child properties.
Also, for the answers, don't use a foreach, use a for loop. Like:
#for(var i=0;i<Answers.Count;i++)
{
<input type="radio" name="#Html.NameFor(a=>a.Answers[i].answer.Value)" value="#Model.Answers[i].anwser.Value" />
}
or
#for(var i=0;i<Answers.Count;i++)
{
#Html.RadioButtonFor(a=>a.Answers[i].answer,Model.Answers[i].answer.Value)
}
Although, that may not be correct either because Answers is a collection of a complex type as well, and you didn't share it's definition.
All in all, I don't think you really need (or want) to post back the entire Model back anyhow. Why not just post back the question ID, and the selected Answer?
Related
I am working on a project "Online examination system". User can give MCQ test here.
Now in my database I have a table named "Question" which contains questions of every subject in the database. When the user selects a subject for an exam, the questions of that subject are stored in var a by where query. Now that var a is passed to return view.
Then in the Exam view, the name of every field is dynamic and differs by ID column in table which is auto increment primary key.
Every MCQ is submitted to Exam_Result() Action method for Checking that its right or wrong.
Now the confusion is in Exam_Result() Action method's parameters... say 20 questions are submitted to Exam_Result, andn how should I handle dynamic names in the parameter of Exam_Result() ?
Exam() Action method in home controller:
public ActionResult Exam(string sname)
{
ProjectDatabaseEntities7 obj = new ProjectDatabaseEntities7();
Question q = new Question();
try
{
var a = obj.Questions.Where(s=>s.Subject_Name.Equals(sname));
return View(a);
}
catch
{
}
return View();
}
Exam.cshtml View:
<form action="/User/Exam_Result">
#foreach (var s in Model)
{
<div class="col-md-12 well form-group">
<label>#s.Question1</label><br>
<input type="text" name="question{#s.Id}" value="#s.Question1" hidden/>
<input type="radio" name="option{#s.Id}">#s.Option1<br />
<input type="radio" name="option{#s.Id}">#s.Option2<br />
<input type="radio" name="option{#s.Id}">#s.Option3<br />
<input type="radio" name="option{#s.Id}">#s.Option4<br />
</div>
}
<input type="submit" value="Submit" class="btn btn-success btn-group-justified" />
</form>
Exam_result() Action Method where the form above is submitting:
public ActionResult Exam_Result(string question, string option)
{
ProjectDatabaseEntities7 obj = new ProjectDatabaseEntities7();
Question q = new Question();
try
{
q = obj.Questions.First(x=> x.Question1.Equals(question));
if(q!=null)
{
if (q.Answer == option)
ViewBag.ans = "right ans";
else
ViewBag.ans = "wrong ans";
}
}
catch
{
return View();
}
return View();
}
Database table Question:
Exam_Result can accept your model as a parameter, allowing it to accept anything that your model defines. When you call into your exam function, and you give it your model, you're giving it all of the properties that it needs at once.
public ActionResult Exam_Result(YourModelClass model)
In your model, you'll just have a property that contains a list of the property that you need N of.
public class YourModelClass
{
public int ExamId {get; set;}
public List<Type> ListName { get; set; }
}
You can access the list as it is a property of the model in your view:
#foreach(var listItem in model.ListName)
{
#listItem
}
To send this information back to the view, you can just return it explicity:
return View("ViewName", model);
Since you're using Razor, all you need to do to define the model in your view is add this to the top of the page:
#model Your.Namespace.YourModelClass
For your comment about having different names and values:
public class YourCustomType
{
public string Name {get; set;}
public var WhateverElse { get; set; }
}
#foreach(YourCustomType item in model.YourList)
{
//item.Name
//item.WhateverElse
}
You model binding is wrong.
First of all don't use entity objects in View page, instead create a custom View model and map entity objects and bind it to view page.
You form <form action="/User/Exam_Result"> submits a list of Model (question) but your action only accepts one question (question/option parameters) which is wrong. You should change Exam_Result action parameter to accept a list of questions
I am unable to make a simple MVC 3 controller/view/model program work with an ActionResult method that includes the Bind attribute with a Prefix property.
One example that I did try could populated the parameter when I call the action method from the URL.
Here is that example controller followed by its view:
//Controller
//public ActionResult PrefixExample(int number = 0)
public ActionResult PrefixExample([Bind(Prefix="okay")]int? number)
{
return View(number);
}
//View
#model Int32?
#{
ViewBag.Title = "Example";
}
<h2>Example</h2>
#using (Html.BeginForm())
{
if (#Model.HasValue)
{
<label>#Model.Value.ToString()</label>
} else {
<label>#Model.HasValue.ToString()</label>
}
<input type="submit" value="submit" />
}
If I use this url http://localhost/MVCApp/Home/Example?okay=3 the parameter, number, is populated. If I use this url http://localhost/MVCApp/Home/Example?number=3, the parameter isn't populated. Interestingly, with the first url, when I view source, the prefix okay doesn't show up.
If I uncomment the first line of my controller and comment out the second line, the opposite is true: the url with okay won't populate number but the second url using number will populate number in the controller.
I would like to know how to make the following example accept a url and correctly set the "view source" prefix. Here is a possible url http://localhost/MVCApp/Home/SpecificPerson?PersonId=0&FirstName=Joe&LastName=Doe
Note, that if I remove the Bind attribute from the controller method, the above url will work with the MVC app below.
Here is my model/controller/view:
//model:
namespace MVCApp.Models
{
public class Person
{
[HiddenInput(DisplayValue = false)]
public int PersonId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
}
//controller
namespace MVCApp.Controllers
{
public class HomeController : Controller
{
public ActionResult SpecificPerson([Bind(Prefix = "myPerson")]Person aPerson)
{
return View("SpecificPerson", aPerson);
}
}
}
//view
#model MVCApp.Models.Person
#{
ViewBag.Title = "SpecificPerson";
}
<h2>SpecificPerson</h2>
#Html.EditorForModel();
<br />
#Html.EditorFor(m => m);
I would like to see the above example work. Anyone who could show me why it doesn't work as I expect or what I can do to make it work this way would be greatly appreciated.
Thank you in advance.
I think the EditorForModel brought you a bit off track. If you check the html that is generated by this helper you will see that it's not wrapped in a form. Besides that I think the EditorForModel will not serve you as much as you would like to. You can also get it to work correctly without specifying the Bind prefix.
//model
public class Person
{
public int Id {get;set;}
public string FirstName {get;set;}
public string LastName {get;set;}
}
//controller
public class HomeController : Controller
{
public ActionResult Index(Person person)
{
if("POST".Equals(Request.HttpMethod))
{
//for example do some validation and redirect
}
return View(person);
}
}
//view
#model Application.Models.Person //only as example use own
#using(Html.BeginForm("Index","Home", FormMethod.POST))
{
#Html.HiddenFor(x=> x.Id)
<div>
#Html.LabelFor(x=> x.FirstName)
#Html.TextBoxFor(x=> x.FirstName)
</div>
<div>
#Html.LabelFor(x=> x.LastName)
#Html.TextBoxFor(x=> x.LastName)
</div>
<input type="submit" value="Do a post request"/>
}
Also if you use a url like /Home/Index?Id=9 and you look the HTML code you will see that there will be a element with input type=hidden and the value of 9. You could also use two actionresults to split your logic with [HttpGet] and [HttpPost] as attribute of your Action.
And as last I recommend you to check out the newer versions of MVC; MVC 5 is already out...
What request would a <input type="checkbox" name="chkbx" checked > return?
Normally I would do something like this:
if(Request["chkbx"] == true)
{
}
But that doesn't work with checkboxes.
In direct response to your question, the key returns true,false as a string in that case. So this is what you want:
if ((Request["chkbx"] as string).Contains("true")) {
}
It is just "false" if not checked.
Ideally though, you would create a model..
class Model {
public bool IsChecked { get; set; }
}
..make your view strongly typed:
#model Your.Namespace.Model
Then use the ...For helpers to bind your model:
#using (Html.BeginForm()) {
#Html.CheckBoxFor(x => x.IsChecked)
<input type="submit" />
}
Then in your action method.. you do this:
[HttpPost]
public ActionResult YourMethod(Model model) {
if (model.IsChecked) {
// your code here
}
}
You could try adding the 'id' property in your HTML and using that instead of the 'name' property
I'm populated a partial view with a strongly-typed model. In this partial view is a form. When I submit the form it tells me that objects inside of my model are null, even though they are not because the partial view rendered all elements based on that same model.
More specifically, I'm having trouble passing back all of my checkboxes. If you look at my controller you can see that I check to see if CompanyOptions is null, and every time I run the program it prints STUFF IS NULL, meaning that it's null.
Model:
public class Company
{
public string Name { get; set; }
public string DatabaseName { get; set; }
public CompanyOptions CompanyOptions;
}
public class CompanyOptions
{
public CompanyLicenseOptions CompanyLicenseOptions { get; set; }
}
public class CompanyLicenseOptions
{
public List<CompanyLicenseOption> CompanyLicenseOptionsList;
}
View:
#using (Html.BeginForm("Action", FormMethod.Post))
{
for (int i = 0; i < Model.CompanyOptions.CompanyLicenseOptions.CompanyLicenseOptionsList.Count; i++)
{
#Html.CheckBoxFor(model => model.CompanyOptions.CompanyLicenseOptions.CompanyLicenseOptionsList[i].IsLicensed, checkboxHtmlAttributes);
<label for="#Model.CompanyOptions.CompanyLicenseOptions.CompanyLicenseOptionsList[i].LicenseName">#Model.CompanyOptions.CompanyLicenseOptions.CompanyLicenseOptionsList[i].LicenseName</label>
<br/>
}
#Html.HiddenFor(model => model.DatabaseName)
<input id="submit_licenses" type="submit" style="display:none;" />
}
Controller:
[HttpPost]
public void Action(Company model)
{
System.Diagnostics.Debug.WriteLine("STUFF:" + model.DatabaseName);
if(model.CompanyOptions!=null)foreach (var item in model.CompanyOptions.CompanyLicenseOptions.CompanyLicenseOptionsList) System.Diagnostics.Debug.WriteLine("STUFF:" + item);
else System.Diagnostics.Debug.WriteLine("STUFF IS NULL");
}
Generated HTML:
<input class="licenses" data-val="true" disabled="" id="CompanyOptions_CompanyLicenseOptions_CompanyLicenseOptionsList_0__IsLicensed" name="CompanyOptions.CompanyLicenseOptions.CompanyLicenseOptionsList[0].IsLicensed" type="checkbox" value="true" /><input name="CompanyOptions.CompanyLicenseOptions.CompanyLicenseOptionsList[0].IsLicensed" type="hidden" value="false" />
The Irrelevant JS
$('#save_licenses').click(function () {
swap_licenses(true);
$('#submit_licenses').click();
});
POST:
Request URL:http://localhost:3080/Controller/Action
Request Method:POST
Status Code:200 OK
CompanyOptions.CompanyLicenseOptions.CompanyLicenseOptionsList[0].IsLicensed:false
CompanyOptions.CompanyLicenseOptions.CompanyLicenseOptionsList[1].IsLicensed:false
CompanyOptions.CompanyLicenseOptions.CompanyLicenseOptionsList[2].IsLicensed:false
CompanyOptions.CompanyLicenseOptions.CompanyLicenseOptionsList[3].IsLicensed:false
CompanyOptions.CompanyLicenseOptions.CompanyLicenseOptionsList[4].IsLicensed:false
CompanyOptions.CompanyLicenseOptions.CompanyLicenseOptionsList[5].IsLicensed:false
CompanyOptions.CompanyLicenseOptions.CompanyLicenseOptionsList[6].IsLicensed:false
CompanyOptions.CompanyLicenseOptions.CompanyLicenseOptionsList[7].IsLicensed:false
CompanyOptions.CompanyLicenseOptions.CompanyLicenseOptionsList[8].IsLicensed:false
CompanyOptions.CompanyLicenseOptions.CompanyLicenseOptionsList[9].IsLicensed:false
CompanyOptions.CompanyLicenseOptions.CompanyLicenseOptionsList[10].IsLicensed:false
CompanyOptions.CompanyLicenseOptions.CompanyLicenseOptionsList[11].IsLicensed:false
CompanyOptions.CompanyLicenseOptions.CompanyLicenseOptionsList[12].IsLicensed:false
DatabaseName:myDb
<input class="licenses" data-val="true" disabled="" id="CompanyOptions_CompanyLicenseOptions_CompanyLicenseOptionsList_0__IsLicensed" name="CompanyOptions.CompanyLicenseOptions.CompanyLicenseOptionsList[0].IsLicensed" type="checkbox" value="true" /><input name="CompanyOptions.CompanyLicenseOptions.CompanyLicenseOptionsList[0].IsLicensed" type="hidden" value="false" />
Here's your problem:
disabled=""
Your checkbox is disabled, so nothing will ever get sent to the server. That's how HTML works. Disabled elements are never sent. So get rid of this attribute.
If you want to prevent the user from modifying the value, and yet the initial value get sent to the server use the readonly attribute, not disabled.
Also another problem I see with your code is with the CompanyLicenseOptionsList collection field. It should be a property with public getter and setter:
public class CompanyLicenseOptions
{
public List<CompanyLicenseOption> CompanyLicenseOptionsList { get; set; }
}
Same stands true for your CompanyOptions field (you have defined it as a field, whereas it should be a property):
public class Company
{
public string Name { get; set; }
public string DatabaseName { get; set; }
public CompanyOptions CompanyOptions { get; set; }
}
UPDATE:
Now that you have fixed the problem with your missing getters and setters, all that's left is make sure that all the models intervening in this object graph have default (parameterless) constructors. That's a requirement if you want they to appear as action argument because otherwise the default model binder wouldn't know how to instantiate them. If for some reason you cannot add a default constructor to all your objects, I would very strongly recommend you revise your object hierarchy and start using view models right away.
You should use a foreach loop instead of a simple for, this way:
#using (Html.BeginForm("Action", FormMethod.Post))
{
foreach (var option in Model.CompanyOptions.CompanyLicenseOptions.CompanyLicenseOptionsList)
{
#Html.CheckBoxFor(o => o.IsLicensed, checkboxHtmlAttributes);
<label for="#option.LicenseName">#option.LicenseName</label>
<br/>
}
#Html.HiddenFor(model => model.DatabaseName)
<input id="submit_licenses" type="submit" style="display:none;" />
}
Since the endpoint of all these checkboxes is a List<T>, you'll need to make sure that it is instantiated before use:
public class CompanyOptions
{
public CompanyLicenseOptions CompanyLicenseOptions { get; set; }
}
public class CompanyLicenseOptions
{
public List<CompanyLicenseOption> CompanyLicenseOptionsList;
public CompanyLicenseOptions()
{
CompanyLicenseOptionsList = new List<CompanyLicenseOption>();
}
}
EDIT: To ensure that readers get proper context for this answer and avoid confusion, I've reproduced the OP code above mine.
public ActionResult Create(RecurringTask recurringTask, FormCollection collection, ICollection<string> dayOfTheWeek)
I am trying to loop through the dayOfTheWeek (which is a group of checkboxes) and I am trying to find out which one is true so than I can use that to assemble a string ex:Monday, Tuesday, etc.
I am just having trouble finding a way of looping through my collection to do it. I keep getting can't apply == to of type string to bool error.
var days = dayOfTheWeek.ToString();
foreach (string day in dayOfTheWeek)
{
if(day == true)
{
}
}
recurringTask.DaysOfTheWeek = days;
This is what I am thinking on how to do it. But I imagine someone out there has a way better idea than I do. The day == true gives me that string to bool error and its obvious to why its happening, I just don't know how to get around it.
My view is this:
<input type="checkbox" name="dayOfTheWeek" value="Monday" />
<input type="checkbox" name="dayOfTheWeek" value="Tuesday" />
<input type="checkbox" name="dayOfTheWeek" value="Wednesday" />
<input type="checkbox" name="dayOfTheWeek" value="Thursday" />
<input type="checkbox" name="dayOfTheWeek" value="Friday" />
<input type="checkbox" name="dayOfTheWeek" value="Saturday" />
<input type="checkbox" name="dayOfTheWeek" value="Sunday" />
public ActionResult Create(RecurringTask recurringTask, FormCollection collection, ICollection dayOfTheWeek)
Sorry to say it but that's probably one of the worst action signatures I have ever seen. A mixture of a domain model, a FormCollection and some ICollection<string>.
Use view models, strongly typed views and editor templates (that's probably the 10^4th time I am writing this sentence on StackOverflow in response to questions in the asp.net-mvc tag)! They will make your life so much easier. So a list of days and a corresponding boolean property to indicate whether this day is selected:
public class MyViewModel
{
public IEnumerable<DayOfWeekViewModel> DaysOfWeek { get; set; }
... put any other properties that you consider useful for this view
}
public class DayOfWeekViewModel
{
public string DayOfWeek { get; set; }
public bool IsSelected { get; set; }
}
then a controller:
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new MyViewModel
{
DaysOfWeek = CultureInfo
.CurrentCulture
.DateTimeFormat
.DayNames
.Select(x => new DayOfWeekViewModel
{
DayOfWeek = x,
})
};
return View(model);
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
// model.DaysOfWeek will contain all you need here
// TODO: do some processing
// here you can loop through model.DaysOfWeek to identify which
// days have been selected and take respective actions
// ...
// once you have finished processing you could redirect
return RedirectToAction("success");
}
}
then a corresponding view:
#model MyViewModel
#using (Html.BeginForm())
{
... you could put any other fields from your view model that
will be used by this form here
#Html.EditorFor(x => x.DaysOfWeek)
<input type="submit" value="OK" />
}
and the corresponding editor template (~/Views/Home/EditorTemplates/DayOfWeekViewModel.cshtml):
#model DayOfWeekViewModel
<div>
#Html.CheckBoxFor(x => x.IsSelected) #Html.DisplayFor(x => x.DayOfWeek)
#Html.HiddenFor(x => x.DayOfWeek)
</div>
The error message is revealing. You are trying to compare a string day to true or false. What does it mean for a string to be true or false?
You really want to see if the checkboxes are checked. You claim that dayOfTheWeek is a group of CheckBoxes, but you are calling ToString() on it, which would convert it to a list of strings. What is the code before var days = dayOfTheWeek.ToString(); Where are you declaring dayOfTheWeek?
Once you really have a List<Checkbox>, you really want to iterate through that list to see if each element is checked.
foreach(CheckBox cb in dayOfTheWeek)
{
if(cb.Checked)
{
// Logic
}
}
If you named your checkboxes "dayOfTheWeek", ICollection<string> dayOfTheWeek only contains the selected checkboxes.
So you could just take this list as "result", but I would encourage you to filter the list of days with a list of valid values to prevent malicious attacks.