Enum to Checkboxes in the Model C# MVC4 - c#

Now I was looking to make some validation for some checkbox fields in my model.
I want to create a unique rule that would require at least one checkbox to be true (or checked) in each category to make it valid. I have three different categories in this model.
I was told to approach this with enum as stated here.
I've looked into the situation and it seems a little over my head, because you basically utilize C# to customize your own rules.
Now these are the categories as mentioned in the hyperlink above:
//Disabilities
[Display(Name = "Learning Disabilities")]
public bool LD { get; set; }
[Display(Name = "Developmental Disabilities")]
public bool DD { get; set; }
[Display(Name = "AD/HD")]
public bool ADHD { get; set; }
[Display(Name = "Autism")]
public bool Autism { get; set; }
//Age Group
[Display(Name = "Child")]
public bool child { get; set; }
[Display(Name = "Youth")]
public bool youth { get; set; }
[Display(Name = "Adult")]
public bool adult { get; set; }
//Strategy Type
[Display(Name = "Academic")]
public bool academic { get; set; }
[Display(Name = "Behaviour")]
public bool behaviour { get; set; }
[Display(Name = "Communication")]
public bool communication { get; set; }
[Display(Name = "Social")]
public bool social { get; set; }
Now to approach this I was told to use enum:
public enum Age
{
[Display(Name="Child")
Child,
[Display(Name="Youth")
Youth,
[Display(Name="Adult")
Adult
}
^Do I throw this in the model still?
I know this goes into the model:
[Required]
public Age MyAge { get; set; }
After looking at several other examples I know that the above code is incomplete and I would also have to edit my view. As sad as it sounds, my education has not gone this far in programming so I apologize for my lack of understanding.
But if you could point me in the right direction so I can walk this golden brick road that would be much appreciated
Cheers.

Here is the small prototype I did for you with Enums and CheckBoxes and its validation.
Let your ENUM be -
public static class Data
{
public enum BloodGroup
{
[Description("A+")]
APositive,
[Description("B+")]
BPositive
}
}
Then construct your Enum model, which will hold the basic Checkbox properties -
public class EnumModel
{
public Data.BloodGroup BloodGroup { get; set; }
public bool IsSelected { get; set; }
}
Then construct Enum View Model based on Enum model, which basically have List of Enum Models -
public class EnumViewModel
{
public List<EnumModel> CheckBoxItems { get; set; }
}
Then your Controller Index Action, will construct EnumViewModel and will bind it to Index View -
public ActionResult Index()
{
EnumViewModel model = new EnumViewModel();
model.CheckBoxItems = new List<EnumModel>();
model.CheckBoxItems.Add(new EnumModel() { BloodGroup = Data.BloodGroup.APositive, IsSelected = false });
model.CheckBoxItems.Add(new EnumModel() { BloodGroup = Data.BloodGroup.BPositive, IsSelected = false });
return View(model);
}
Index View will display all the checkboxes and will make a POST to Submit action on click of submit button -
#model MVC.Controllers.EnumViewModel
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
#Html.ValidationSummary();
#using (Html.BeginForm("Submit", "Enum", FormMethod.Post))
{
for (int i = 0; i < Model.CheckBoxItems.Count; i++)
{
#Html.LabelFor(m => m.CheckBoxItems[i].BloodGroup);
#Html.CheckBoxFor(m => m.CheckBoxItems[i].IsSelected);
#Html.HiddenFor(m => m.CheckBoxItems[i].BloodGroup);
}
<input type="submit" value="click"/>
}
In the Submit Action We check for the IsSelected properties of the Enum View Model, if there are none, then we return error to Index View.
public ActionResult Submit(EnumViewModel model)
{
if (!model.CheckBoxItems.Where(p => p.IsSelected).Any())
{
ModelState.AddModelError("CheckBoxList", "Please select atleast one!!!");
return View("Index",model);
}
return View();
}
Output -
On Load -
When we do not select anything and submit the form -

Related

Multiple answer group & answers asp.net MVC

I want to do a single OR multiple answer quiz using radiobuttonfor and checkboxFor, but I cannot make it work. The problem with all example I see is that the Question model also register the SelectedAnswer, but in my case I want each possible answer to be selectable since some questions will have multiples answers, and thus the isSelected property is directly inside the Answer model.
Therefore, for questions with single answers, when I try to create my model using RadioButtonFor(m => m[question].Answers[answerToDisplayId].IsSelected), every answer is in its own group and is not unchecked when I check another answer from that question (basically it behave like a checkBoxFor)
What I currently have: The question model
public enum questionfield
{
Chaser, Beater, Seeker, Contact, Process, Other
};
public enum QuestionDifficulty
{
Basic, Advanced
};
public enum AnswerType
{
SingleAnswer, MultipleAnswer
}
public class Question
{
public int Id { get; set; }
[Required(ErrorMessage = "Question name not valid")]
public string Name { get; set; }
[Required]
public QuestionField Field { get; set; }
[Required]
public QuestionDifficulty Difficulty { get; set; }
[Required]
public bool IsVideo { get; set; }
public string VideoURL { get; set; }
[Required]
public string QuestionText { get; set; }
[Required]
public AnswerType AnswerType { get; set; }
[Required]
public List<Answer> Answers { get; set; }
[Required]
public String AnswerExplanation { get; set; }
Answer model :
public class Answer
{
public int Id { get; set; }
public String Answertext { get; set; }
public Boolean IsTrue { get; set; }
public Boolean IsSelected { get; set; }
}
The view :
<div>
<!-- For each Question, display a new div with the Title, the question code, the question text, the video if there is one, then the possible answers depending on the type of answers-->
#using(Html.BeginForm("QuizzResult", "Home"))
{
for(int i = 0; i < Model.Count; i++)
{
<div class="QuizzQuestion">
<div class="QuizzQuestionTitle">#Model[i].Id : #Model[i].Name</div> #Html.HiddenFor(m => Model[i].Id)
<div class="QuizzQuestiontext">#Model[i].QuestionText</div>
#if(#Model[i].IsVideo)
{
<div class="QuizzQuestionVideoContainer">
<iframe class="QuizzQuestionVideo" id="ytplayer" type="text/html"
src="#Model[i].VideoURL"
frameborder="0"></iframe>
</div>
}
<div class="RadioButtonAnswers">
#if (#Model[i].AnswerType == QRefTrain3.Models.AnswerType.SingleAnswer)
{
for (int j = 0; j < Model[i].Answers.Count; j++)
{
#Model[i].Answers[j].Answertext #Html.RadioButtonFor(m => m[i].Answers[j].IsSelected, true)
#Html.HiddenFor(m => Model[i].Answers[j].IsTrue)
}
}
</div>
</div>
}
<input type="submit" value="Validate Answers"/>
}
</div>
The controller :
[HttpPost]
public ActionResult QuizzResult(List<Question> answers)
{
foreach(Question a in answers)
{
var b = Request.Form[a.Id.ToString()];
}
Result result = new Result();
foreach (Question q in answers)
{
result.QuestionsAskedIds.Add(q.Id);
if (Question.IsGoodAnswer(q))
{
result.GoodAnswersIds.Add(q.Id);
}
}
if (User.Identity.IsAuthenticated)
{
result.User = Dal.Instance.GetUserByName(HttpContext.User.Identity.Name);
Dal.Instance.CreateResult(result);
}
return View("QuizResult", result);
}
What would be the good way to do this? Thank you!
In case someone will see this :
The solution I found was to change the model : Instead of having one IsSelected parameter per answer, add a List selectedAnswers to your Question model. Then, in the view, add your radiobutton like this :
#Html.RadioButtonFor(m => m[i].SelectedAnswers, Model[i].Answers[j].Id)
You will store the id of each selected answer for this question ine the SelectedAnswers list. You can then create your results using this data.

Get selected CheckBox values in controller

I have a dormitory adding page and this dormitories can have features so I want to use CheckBox list for this.
There is a list of all features a dormitory can have.
public class DormFeatureModel
{
[Key]
public int FeatureID { get; set; }
public string FeatureName { get; set; }
public List<DormHasFeatureModel> DormHasFeature { get; set; }
}
Here, too, are features that a dormitory has.
public class DormHasFeatureModel
{
[Key]
public int HasFeatureID { get; set; }
[Required]
public int FeatureID { get; set; }
[Required]
public int DormID { get; set; }
public virtual DormModel Dorm { get; set; }
public virtual DormFeatureModel DormFeature { get; set; }
}
I can get features list in razor as checkbox
but I can't get selected checkboxes id list(so, FeatureID)
How can I get list in controller ?
First, add a ViewModel that correlates the Checked boolean with the FeatureId.
public class SelectedFeatureViewModel {
public bool Checked { get; set; } // to be set by user
public int FeatureID { get; set; } // to be populated by GET action
public string FeatureName { get; set; } // to be populated by GET action
}
The GET action creates the main ViewModel and initializes the list of available features (DormOptions).
public class CreateDormViewModel {
// used to render the checkboxes, to be initialized in GET controller action
// also used to bind the checked values back to the controller for POST action
public ICollection<SelectedFeatureViewModel> DormOptions { get; set; }
}
In the Razor markup, bind the checkboxes to the DormOptions collection:
#model CreateDormViewModel
#using (Html.BeginForm("CreateDorm", "DormAdministration", FormMethod.Post)) {
// use for loop so modelbinding to collection works
#for (var i = 0; i < Model.DormOptions.Count; i++) {
<label>#Model.DormOptions[i].FeatureName</label>
#Html.CheckBoxFor(m => m.DormOptions[i].Checked)
// also post back FeatureId so you can access it in the controller
#Html.HiddenFor(m => m.DormOptions[i].FeatureID)
// post back any additional properties that you need to access in the controller
// or need in order to redraw the view in an error case
#Html.HiddenFor(m => m.DormOptions[i].FeatureName)
}
}
In the CreateDorm POST action, the checkbox values are bound to the ViewModel property you gave in the CheckBoxFor lambda, i.e. the Checked property in the DormOptions collection.
[HttpPost]
public ActionResult CreateDorm(CreateDormViewModel postData) {
var selectedFeatureIds = new List<int>();
foreach (var option in postData.DormOptions) {
if (option.Checked) {
selectedFeatureIds.Add(option.FeatureID);
}
}
// ...
}
You can get the list by using the name of the checkboxes, let say your name of the checkboxes is chklstfeatureid then in controller you can get list like below
public actionresult createdorm(list<int> chklstfeatureid)
{
}
Thanks

Model Binding Doesn't Match Value

I'm having a problem I can't seem to figure out here and I've done a fair amount of searching:
In my model (a list of objects) I have the following output:
<input data-val="true" data-val-number="Please enter a number." data-val-required="The ModelID field is required." id="Students_19__ModelID" name="Students[19].ModelID" type="hidden" value="62">
If you look closely here this value is 62 (I've bound a second object just to check myself and I see that it is also 62:
<input data-val="true" data-val-number="Please enter a number." id="Students_19__storage_ID" name="Students[19].storage.ID" type="hidden" value="62">
In the same view I'm seeing this:
#Html.LabelFor(_ => Model.Students[i].Show, "ID: " + Model.Students[i].storage.ID)
producing:
ID: 63
In summary:
#Html.HiddenFor(_ => Model.Students[i].storage.ID)
#Html.LabelFor(_ => Model.Students[i].Show, "ID: " + Model.Students[i].storage.ID)
OR
#Html.HiddenFor(m => m.Students[i].storage.ID)
#Html.LabelFor(m => m.Students[i].Show, "ID: " + Model.Students[i].storage.ID)
produces->
<input data-val="true" data-val-number="Please enter a number." id="Students_19__storage_ID" name="Students[19].storage.ID" type="hidden" value="62">
<label for="Students_19__Show">ID: 63</label>
Obviously 62 != 63 and this is throwing off the model in the controllers. Does anyone have an idea of what could be causing this? I feel like something is being converted poorly but I'm out of ideas at this point. I've seen this behavior before and shuffling around the loading order of the hidden fields has fixed it but this obviously isn't a good solution.
EDIT: Per comments:
The structure of the object is pretty tame: (this is the "storage" variable)
public class EducationGoalModel
{
public int? ID { get; set; }
public decimal Amount { get; set; }
public string Name { get; set; }
public string Category { get; set; }
//Education
public int StudentAge { get; set; }
public int StudentBegin { get; set; }
public int StudentYears { get; set; }
[DisplayFormat(DataFormatString = "{0:P2}", ApplyFormatInEditMode = true)]
[Percentage] //Custom annotation, nothing silly here
[Required(ErrorMessageResourceName = "Required", ErrorMessageResourceType = typeof(Resources.Errors))] //Error is not triggered otherwise the ID wouldn't work in the "workaround"
public decimal Inflation { get; set; }
// This is just used by another module that needs annotations waaaay down the line, these values are unused.
public decimal TargetAmount { get { return Calculations.FutureValue(this.YearOfCompletion - DateTime.Now.Year, this.Inflation, this.Amount); } }
public byte RiskToleranceLevel { get; private set; }
public int YearOfCompletion { get; set; }
public bool Hide { get; set; }
public EducationGoalModel()
{
this.Category = FinanceGoalTargetCategory.EDUCATIONAL;
}
public EducationGoalModel(EducationGoal goal)
{
//The goal is just the entity I'm using, this is a light wrapper around it.
ID = goal.ID;
Amount = goal.Amount;
Name = goal.Name;
Category = goal.Category;
StudentAge = goal.StudentAge;
StudentBegin = goal.StudentBegin;
StudentYears = goal.StudentYears;
Inflation = goal.Inflation;
Hide = goal.Hide;
}
public void CopyTo(EducationGoal goal)
{ //SNIP.... we don't really care about this method, it isn't called
}
Here's the view Model:
public class EducationGoalInputModel : InputModelBase
{
public CurrencySliderModel AmountSlider { get; set; }
public EducationGoalModel storage { get; set; }
public int ModelID { get; set; }
public int StudentAge { get; set; }
public int StudentBegin { get; set; }
public int StudentYears { get; set; }
[MaxLength(20, ErrorMessageResourceName = "FinancialGoalsNameLength", ErrorMessageResourceType = typeof(Resources.Errors))]
public string Name { get; set; }
[DisplayFormat(DataFormatString = "{0:P2}", ApplyFormatInEditMode = true)]
[Percentage]
[Required(ErrorMessageResourceName = "Required", ErrorMessageResourceType = typeof(Resources.Errors))]
public decimal Inflation { get; set; }
... plain constructor (no args), a second to construct and establish the "storage" object which is the one used for the above output.
Here's the base class, nothing interesting here really:
public abstract class InputModelBase { //a simple class that has
public abstract void UpdateAmounts(int age);
//and a couple other utility methods for the user interface (we support IE8 so a lot of UI information is stored)
}
EDIT: Per Hacks: because this is MVC (this works fine, but is terrible for maintenance and indicates a bigger problem)
<input type="hidden" name="#String.Format("Students[{0}].storage.ID", i)" id="#String.Format("Students_{0}__storage_ID", i)" value="#Model.Students[i].storage.ID" />
This outputs the correct values of value="63" using the example above.
EDIT #2
Based on feedback from the comments below *(thank you!) it looks like the ModelState doesn't agree with the Model. There's nothing obvious modifying the ModelState in the View that I can find but I've inherited this project and there's probably some extension method doing something that I need to track down. The values in this particular test 5. The ModelState shows the wrong value and thus the binding is wrong. This has not fixed the issue but is basically an accepted answer as I now know where to look.
Thanks for your help.

MVC3 POST model binding not working for particular complex model

For some reason, when I post this view model back to the controller and add in the model for binding, it ends up being null. The application that I am working with is a massive one. Also I haven't written much of the code so this model is massive so I will just add the parts that matter, but could other properties be preventing the model binding?
I do know that it has been working but in the last little bit it started not. Maybe it's not even something with the model, would just love some help debugging it.
POST Action:
[HttpPost]
public ActionResult Categories(int applicationId, SqsApplicationViewModel model)
{
// Save away the ids they chose
_sqsApplicationCategoryService.SaveCategories(applicationId, model.Display_Categories.Where(i => i.Selected).Select(i => i.CategoryId).ToList());
// Complete the step
_sqsApplicationStepService.CompleteStep(applicationId, SqsStep.Categories);
return RedirectToAction("Documents");
}
View Model:
public class SqsApplicationViewModel : IMappable
{
public int Id { get; set; }
public int SupplierId { get; set; }
public int? SqsApprovalLevelId { get; set; }
// Other properties .....
public List<SqsChosenCategoryViewModel> Display_Categories { get; set; }
// Other properties .....
}
public class SqsChosenCategoryViewModel
{
public int CategoryId { get; set; }
public string Name { get; set; }
public string CategoryAmountString { get; set; }
public bool Selected { get; set; }
public IList<SqsDocumentComplianceViewModel> Documents { get; set; }
}
View:
#using (Html.BeginForm())
{
#Html.HiddenFor(m => m.Id)
#if (Model.Display_Categories != null && Model.Display_Categories.Count() > 0)
{
for (var i = 0; i < Model.Display_Categories.Count; i++)
{
#Html.HiddenFor(m => m.Display_Categories[i].CategoryId)
#Html.CheckBoxFor(m => m.Display_Categories[i].Selected)
#Model.Display_Categories[i].Name
}
}
}
Also, the values being sent back in firebug are:
Id:1061
Display_Categories[0].CategoryId:4
Display_Categories[0].Selected:true
Display_Categories[0].Selected:false
Display_Categories[1].CategoryId:1
Display_Categories[1].Selected:false
Display_Categories[2].CategoryId:2
Display_Categories[2].Selected:false
Display_Categories[3].CategoryId:3
Display_Categories[3].Selected:false
Display_Categories[4].CategoryId:6
Display_Categories[4].Selected:true
Display_Categories[4].Selected:false
Display_Categories[5].CategoryId:8
Display_Categories[5].Selected:false
Display_Categories[6].CategoryId:10
Display_Categories[6].Selected:false
Display_Categories[7].CategoryId:7
Display_Categories[7].Selected:false
Display_Categories[8].CategoryId:9
Display_Categories[8].Selected:false
Display_Categories[9].CategoryId:11
Display_Categories[9].Selected:false
Display_Categories[10].CategoryId:5
Display_Categories[10].Selected:true
Display_Categories[10].Selected:false
-------------EDIT----------------
I tried using the following test models and it worked. Is it possible that another property in the Model could be hindering the binding? I added some random ones in these too and it still worked.
public class TestViewModel
{
public int Id { get; set; }
public IList<TestSubViewModel> Display_Categories { get; set; }
public string TestProp { get { return "asdfasdfasdf"; } }
public TestSubViewModel TestGetFirst { get { return this.Display_Categories.FirstOrDefault(); } }
}
public class TestSubViewModel
{
public int CategoryId { get; set; }
public string Name { get; set; }
public string CategoryAmountString { get; set; }
public bool Selected { get; set; }
public IList<SqsDocumentComplianceViewModel> Documents { get; set; }
}
So I'm just going to answer my own question, though it isn't solved as much as there is another way to do it.
I believe that when you typehint the model and it binds it, in the background it uses "TryUpdateModel()" and so I just called this in the controller and for some reason it worked. Not sure if I miss out on anything else by doing it this way, but it has worked for me.
Also you can debug what might be the issue by doing it this way by the following:
var model = new ViewModel();
var isSuccess = TryUpdateModel(model);
if (!isSuccess)
{
foreach (var modelState in ModelState.Values)
{
foreach (var error in modelState.Errors)
{
Debug.WriteLine(error.ErrorMessage);
}
}
}
Taken from this post: How to find the exceptions / errors when TryUpdateModel fails to update model in asp.net mvc 3

MVC Add and remove items from subcollection

I'm having trouble binding data to a collection item's collection (I'm also having trouble wording my problem correctly). Let's just make thing easier on everyone by using an example with psudo models.
Lets say I have the following example models:
public class Month()
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Week> Weeks { get; set; }
}
public class Week()
{
public int ID { get; set; }
public int MonthID { get; set; }
public String Name { get; set; }
public virtual ICollection<Day> Days { get; set; }
}
public class Day()
{
public int ID { get; set; }
public String Name { get; set; }
}
...and an example viewmodel:
public class EditMonthViewModel()
{
public Month Month { get; set; }
public List<Week> Weeks { get; set; }
public List<Day> AllDays { get; set; }
}
The purpose of the Edit Action/View is to enable users to edit a month, the weeks assigned to the month, and add and remove days from weeks of a certain month. A view might help.
#model myProject.ViewModels.EditMonthViewModel
//...
#using (Html.BeginForm())
{
//Edit Month Stuff...
#for(int i = 0; i < Model.Weeks.Count(); i++)
{
<h2>#Model.Weeks[i].Name</h2>
#Html.EditorFor(model => Model.Weeks[i].Name)
//loop through all possible days
//Select only days that are assigned to Week[i]
#for(int d = 0; d < Model.AllDays.Count(); d ++)
{
//This is the focus of this question.
//How do you bind the data here?
<input type="checkbox"
name="I have no idea"
#Html.Raw(Model.Weeks[i].Days.Contains(Model.AllDays[d]) ? "checked" : "") />
}
}
}
Controller Action methods
public ActionResult Edit(int id)
{
var viewModel = new EditMonthViewModel();
viewModel.Month = db.Months.Find(id);
viewModel.Weeks = db.Weeks.Where(w => w.MonthID == id).ToList();
viewModel.AllDays = db.Days.ToList();
}
[HttpPost]
public ActionResult Edit(EditMonthViewModel viewModel)
{
var monthToUpdate = db.Months.Find(viewModel.Month.ID);
//...
if(viewModel.Weeks != null)
{
foreach (var week in viewModel.Weeks)
{
var weekToUpdate = monthToUpdate.Weeks.Single(w => w.ID == week.ID);
//...
/*So, I have a collection of weeks that I can grab,
but how do I know what was selected? My viewModel only has a
list of AllDays, not the days selected for Week[i]
*/
}
}
How can I ensure that when I submit the form the selected days will bind to the week?
It looks like the easiest thing to do is to make it a goal for your form to populate a data structure of the type IEnumerable<DayModel>, where DayModel is defined as:
public class DayModel
{
public int WeekId { get; set; }
public int DayId { get; set; }
public bool IsIncluded { get; set; }
}
You could keep your Razor code as is for the most part, but then when it comes to rendering the checkboxes, you can do something like this:
#{
var modelIdx = 0;
}
// ...
<input type="hidden" name="days[#modelIdx].WeekId" value="#Model.Weeks[i].Id" />
<input type="hidden" name="days[#modelIdx].DayId" value="#Model.AllDays[d].Id" />
<input type="checkbox" name="days[#modelIdx].IsIncluded" value="#(Model.Weeks[i].Days.Contains(Model.AllDays[d]) ? "checked" : "")" />
#{ modelIdx++; }
Then, your controller action you post to could have this signature:
[HttpPost]
public ActionResult Edit(IEnumerable<DayModel> days)
{
//...
}
Something that helps me is to never confuse view models, which should only be used for the model for views (GET actions generally) and non-view models (what we call plain models). Avoid having your POST actions try to bind to view models, and it will simplify your life greatly.

Categories

Resources