how to handle a list of checkbox? - c#

I am developing a page which display data from service on the View and allow users to filter it.
There is a column for Countries, which allow users to filter.
I am not able to figure out a way to create a list of checkbox so that I can grab all selected values in one param like string[] countries (in the action method).
Can't use classic way :
<input type="checkbox" name="countries" value="USA" />USA<br />
<input type="checkbox" name="countries" value="Canada" />Canada<br />
This does pass the values in URL but doesn't set them back on post back (keeping checked onpostback).
I tried using checkboxlist (http://goo.gl/TUvZzu) but seemed to complicated for my Modal.
Since mine is a very straight forward Model :
public string Title { get; set; }
public string Website { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public string Zip { get; set; }
public string State { get; set; }
public string Country { get; set; }
I appreciate your time and help.

You need to include a collection of countries within your view model to hold the selected values and allow them to be sent in the post.
I would also create a Country object to hold the Id, Name and Selected values.
In order for the model to be posted back you need to index each item in the view, this allows the model binder to pick it up.
Models
public class AModel
{
public AModel()
{
Countries = new List<Country>();
}
public string Title { get; set; }
public string Website { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public string Zip { get; set; }
public string State { get; set; }
public string Country { get; set; }
// Country collection
public List<Country> Countries { get; set; }
}
public class Country
{
public int ID { get; set; }
public string Name { get; set; }
public bool Checked { get; set; }
}
View for loop
#for(var i = 0; i < Model.Countries.Count; i++)
{
<dt>
#Html.HiddenFor(m => Model.Countries[i].ID)
#Html.HiddenFor(m => Model.Countries[i].Name)
#Html.CheckBoxFor(m => Model.Countries[i].Checked)
</dt>
<dd>
#Model[i].Name
</dd>
}
Note the for loop instead of foreach to enable model binding and the hidden fields to allow the values to be posted back to the controller
http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/
Controller post
[HttpPost]
public ActionResult Index(AModel model)
{
//All the selected countries are available in the model
return View(model);
}
Working example

Related

ASP.NET nested list of models is not binding

I'm trying to post a list of models to the server, using ASP.NET's model binding and manipulating a bunch of values with JavaScript. When I send the values to the server, this is what I get:
model.inventory.processed_items[0].id: GA-6570
model.inventory.processed_items[0].event:
model.inventory.processed_items[0].subevent:
model.inventory.processed_items[0].restrict_marking:
model.inventory.processed_items[0].cecp_string:
model.inventory.processed_items[0].discrepancies:
model.inventory.processed_items.Index: 0
model.inventory.processed_items[1].id: GD-1000
model.inventory.processed_items[1].event:
model.inventory.processed_items[1].subevent:
model.inventory.processed_items[1].restrict_marking:
model.inventory.processed_items[1].cecp_string:
model.inventory.processed_items[1].discrepancies:
model.inventory.processed_items.Index: 1
These are my model classes that I'm binding to (I've omitted any fields that don't really matter to the question):
public class PackageViewModel
{
public InventoryViewModel inventory { get; set; }
}
public class InventoryViewModel
{
public List<ProcessedItemViewModel> processed_items { get; set; }
}
public class ProcessedItemViewModel
{
public string id { get; set; }
public int #event { get; set; }
public string subevent { get; set; }
public string cecp_string { get; set; }
public string restrict_marking { get; set; }
public string discrepancies { get; set; }
public string highest_classification { get; set; }
public int occurences_count { get; set; }
public IEnumerable<ProcessedOccurenceViewModel> occurences { get; set; }
}
public class ProcessedOccurenceViewModel
{
public string text { get; set; }
public string security_num { get; set; }
public Nullable<int> media_count { get; set; }
public string classification { get; set; }
}
This is my controller:
[HttpGet]
public ActionResult Create()
{
var inventoryVM = new InventoryViewModel
{
processed_items = new List<ProcessedItemViewModel>()
};
var packageVM = new PackageViewModel {
inventory = inventoryVM
};
return View(packageVM);
}
[HttpPost]
public ActionResult Create(PackageViewModel packageVM)
{
if (ModelState.IsValid)
{
...
}
}
When I check packageVM in debugger, the values are not bound to the view model. However, other values excluding this nested list of models are included in the packageVM model during the POST request. I don't understand why this portion is not binding because I have supplied indices and also passed in an empty list to the view.
The property names for the values you are sending do not match the model you are binding to. PackageViewModel does not contain a property named model (it contains one named inventory), so instead of
model.inventory.processed_items[0].id: GA-6570
it needs to be
inventory.processed_items[0].id: GA-6570
An easy way to think about this is to consider how you would access the value of a property of the model in the POST method
public ActionResult Create(PackageViewModel packageVM)
{
// get the id of the first item in processed_items
string id = packageVM.inventory.processed_items[0].id
Because the parameter in the method is named packageVM, just drop that prefix, (i.e. becomes inventory.processed_items[0].id), and that is what the name of the data needs to be in order to bind.
As a side note, it you are using the strong typed ***For() methods inside a for loop to generate your form controls based on your model, they will generate the correct name attributes, and you can just use $('form').serialize() to correctly generate the data to be sent via your ajax call.

Trying to access List<> properties from Html.DropDownListFor()

Probably I'm doing this the wrong way, but this is a sample of the code I'm dealing with:
ViewModel
public class TasDataView
{
public int nTas { get; set; }
public string codTas { get; set; }
public decimal nValue { get; set; }
public bool nState { get; set; }
public System.DateTime nDate { get; set; }
public IEnumerable<Segments> ListSegments { get; set; }
public IEnumerable<Tas> ListTas { get; set; }
}
Segments class
public class Segments
{
public int segmentValue { get; set; }
public string segmentCode { get; set; }
public bool nState { get; set; }
public System.DateTime nDate { get; set; }
}
So, when I call the View with my controller, I send an TasDataView object as parameter:
public ActionResult AddTas()
{
TasDataView TDV = new TasDataView();
SegmentsManager SM = new SegmentsManager();
TDV.ListSegments = SM.DataSegments();
return View(TDV);
}
And here is the problem I'm facing. I need all the values of ViewModel for this View to work. So, for example, I have:
#model Project.Models.ViewModel.TasDataView
#Html.EditorFor(model => model.nValue, new { htmlAttributes = new { #class = "form-control" } })
And that will create the box for inserting whatever I want into nValue. But what if I want to access the ListSegments properties (from Segments class) that I sent to the View, so I can make a DropDown List ?
I can't do something like:
#Html.DropDownListFor(x => x.ListSegments.segmentValue, new SelectList(Model.ListSegments));
because all the ListSegments (like segmentValue) properties "are not in" TasDataView, but in Segments class.
How can I reorganize my DataView to be manageable for what I want? Instead of a List<> I should have plain variables?
I guess you're doing it wrong. The first argument of DropDownListFor is an expression that represents the property of the model that receives the value of the item selected in the drop down list. It must be some dedicate=d property somewhere on your model. e.g. selectedSegment.
EDIT:
So, if your goal is to present a list of values via model to the user in a drop down list and let the user pick a value, you'll need to:
have a property on the model to store the selected item (segment), e.g. selectedSegment,
properly render your segments as strings to present them.
The simplest solution is given below.
public class TasDataView
{
public int nTas { get; set; }
public string codTas { get; set; }
public decimal nValue { get; set; }
public bool nState { get; set; }
public System.DateTime nDate { get; set; }
public IEnumerable<Segments> ListSegments { get; set; }
public IEnumerable<Tas> ListTas { get; set; }
public Int32 selectedSegment;
}
#Html.DropDownListFor(x => x.selectedSegment, new SelectList(Model.ListSegments.Select(s => s.segmentCode)));
So there are no properties of the list you need to access.

How to post list of model on controller?

There is 1 form on which i will ask for Academic Details for :
Graduation
Post Graduation(Masters)
Professional Qualification.
So far any user say UserId="1" 3 entries will be created in my AcademicMaster each for bachelor,Master(Post Graduation) and Professional Qualification.
My Database AcademicMaster table fields and datamodel:
Id,Qualification(GraduationCourses),Acheievement,UserId
View Model:
public class AcademicViewModel
{
public int Id { get; set; }
public virtual Graduation Graduation{ get; set; }
public virtual PostGraduation PostGraduation{ get; set; }
public virtual ProfessionalQualification ProfessionalQualification{ get; set; }
}
public class Graduation
{
public string BachelorQualification { get; set; }
public string BachelorAchievement { get; set; }
}
public class PostGraduation
{
public string MasterQualification { get; set; }
public string MasterAchievement { get; set; }
}
public class ProfessionalQualification
{
public string ProfessionalQualifications { get; set; }
}
So my View is like this:
#model AcademicViewModel
#{
IEnumerable<SelectListItem> graduationList = ViewBag.GraduationList;
IEnumerable<SelectListItem> postGraduationList = ViewBag.PostGraduationList;
}
#using (Html.BeginForm())
{
<div class="row">
Bachelors
#Html.DropDownListFor(model => model.Graduation.Qualification, graduationList)
</div>
#Html.TextAreaFor(model => model.Graduation.Achievement)
<div class="row">
MASTERS
#Html.DropDownListFor(model => model.PostGraduation.Qualification, postGraduationList)
</div>
#Html.TextAreaFor(model => model.PostGraduation.Achievement)
<div class="row">
PROFESSIONAL QUALIFITCATION
#Html.TextAreaFor(model => model.ProfessionalQualification.ProfessionalQualifications)
</div>
<input type="submit" value="Save">
}
This is my Controller:
[HttpPost]
public ActionResult MyController(AcademicViewModel model)
{
//Actions
}
So is my View Model structure appropriate and how to create 3 entries in AcademicMaster Table??
I will start by saying that having one table may not be the best choice (what happens if later you start adding additional properties which may be applicable to Graduation that are not applicable to Professional - for example YearOfGraduation - you could end up with a huge number of fields, many of which may have null values.
However, if you want one table, then at least add another field so that you can identify if the data is related to Graduation, PostGraduation or Professional. The associated data model for the AcademicMasters table would be
public class AcademicMaster
{
public int ID { get; set; }
public string Type { get; set; } // may be an enum?
public string Qualification { get; set; }
public string Achievement { get; set; }
public int UserID { get; set; }
}
Side note: It might be better to use an enum for the Type property
public enum AcademicType
{
Graduation,
PostGraduation,
Professional
}
There does not seem to be any need to your current Graduation, PostGraduation and ProfessionalQualification models and your view model should be
public class AcademicViewModel
{
public string GraduationQualification { get; set; }
public string GraduationAchievement { get; set; }
public string PostGraduationQualification { get; set; }
public string PostGraduationAchievement { get; set; }
public string ProfessionalAchievement { get; set; }
public IEnumerable<SelectListItem> GraduationList { get; set; }
public IEnumerable<SelectListItem> PostGraduationList { get; set; }
}
Side notes: Its not clear what your current ProfessionalQualifications property is - does that get assigned to the Qualification field or the Acheievement field in the database? Since your using a view model, then it should include the SelectList's rather that using ViewBag.
Then your view will be
#model AcademicViewModel
#using (Html.BeginForm())
{
<h2>Graduation</h2>
#Html.DropDownListFor(m => m.GraduationQualification, Model.GraduationList)
#Html.TextAreaFor(m => m.GraduationAchievement)
... // repeat for PostGraduation and Professional
<input type="submit" value="Save">
}
And the POST method would be
[HttpPost]
public ActionResult MyController(AcademicViewModel model) // should be named Create?
{
var userID = ....
AcademicMaster graduation = new AcademicMaster
{
Type = AcademicType.Graduation,
Qualification = model.GraduationAchievement,
Achievement = model.GraduationAchievement,
UserId = userID;
};
db.AcademicMasters.Add(graduation);
// Repeat for PostGraduation and Professional
db.SaveChanges();
// redirect?
}

Populate checkbox list from enum

I have created a public enum Interests
public enum Interests
{
Sport,
Party,
Gaming
}
And wanted to have a List of Enums in my user-Profile
public class User
{
public string Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public List<Interests> Interests = new List<Interests>;
}
I want to create a Checkbox-Form for my Enum-Values, which finally should get stored in my List, but can't find out how ...
I want to have for each Hobby in enum Interests a Checkbox-Field and every checked Checkbox should get stored in the List .
Try
checkboxID.DataSource = Enum.GetNames(typeof(Interests));
checkboxID.DataBind();
For MVC:
#Html.EnumDropDownListFor(m => m.Interests) in the view

Conditional view of fields in a view model in razor

I have the following view model to query my table:
QuestionViewModel.cs
public enum TypeQuestion {
Long = 1,
Short = 2,
Small = 3,
}
public class QuestionViewModel
{
public string Name { get; set; }
public string LastName { get; set; }
public string Address { get; set; }
public string MaxAge { get; set; }
public string Category { get; set; }
public string Account { get; set; }
public TypeQuestion CurrentTypeQuestion { get; set; }
}
if the query I'm doing is of type:
Long: displays all fields.
Short: displays Name, LastName, Address, MaxAge.
Small: displays Name, LastName.
Is there any way to put some kind of DataAnnotation to determine which fields to display in the view or some other way?, To avoid putting a "what if?" for each field.
Thank you.
This may be overkill, and i'd in fact lean towards #Mystere Man's answer, but this is another option.
Instead of regular primitive types in your ViewModel, set them up to cater for the logic. Looks like Name and LastName are always displayed, whilst Address and MaxAge are conditional.
So, setup your ViewModel like this:
public class QuestionViewModel
{
public string Name { get; set; }
public string LastName { get; set; }
public IEnumerable<ConditionalField> ConditionalFields { get; set; }
public string Category { get; set; }
public string Account { get; set; }
}
public class ConditionalField
{
public string Field { get; set; }
public bool Display { get; set; }
}
In your controller, setup the nested viewmodel and the boolean values for Address and MaxAge ccording to the value of CurrentTypeQuestion.
Then, have your View like this:
/Views/Questions.cshtml
#model QuestionViewModel
#Html.DisplayForModel()
Then create a custom display template (or editor template, if this is a form) for QuestionViewModel:
/Views/DisplayTemplates/QuestionViewModel.cshtml
#model QuestionViewModel
#Html.DisplayFor(model => model.Name)
#Html.DisplayFor(model => model.LastName )
#Html.DisplayFor(model => model.Category)
#Html.DisplayFor(model => model.Account)
#Html.DisplayFor(model => model.ConditionalFields)
Then create another custom display template for ConditionalField:
Views/DisplayTemplates/ConditionalField.cshtml
#model ConditionalField
#if (Model.Display) {
#Html.DisplayForModel()
}
As i said, may be overkill, but in the end, you only have a single if statement in the custom template, no loops, and your main view and first-level template stays clean.
To keep it simple, and avoid complex if logic in your view, just create three different views, with only the data you need in each view. Then select the view in your controller based on the question type.
Based from this link and this link
Controller:
public ActionResult Consulta()
{
return View(new QuestionViewModel());
}
ViewModel:
public enum TypeQuestion {
Long = 1,
Short = 2,
Small = 3,
}
public class QuestionViewModel
{
public string Name { get; set; }
public string LastName { get; set; }
public string Address { get; set; }
public int MaxAge { get; set; }
public string Category { get; set; }
public string Account { get; set; }
public TypeQuestion CurrentTypeQuestion { get; set; }
public bool EnabledField(ModelMetadata field)
{
//check pending implementation
return true;
}
}
View:
#model MySite.QuestionViewModel
#using System.Linq;
#using System.Collections;
#{
ViewBag.Title = "Question";
Layout = "~/Views/Shared/Layout.cshtml";
}
<h2>Question</h2>
#using (Html.BeginForm(new { id = "FormQuestion" }))
{
foreach (var prop in ViewData.ModelMetadata.Properties
.Where(pm => pm.ShowForDisplay && !ViewData.TemplateInfo.Visited(pm) && ViewData.Model.EnabledField(pm)))
{
if (prop.HideSurroundingHtml)
{
Html.Editor(prop.PropertyName);
}
else
{
<div class="editor-label">
#(prop.IsRequired ? "*" : "")
#Html.Label(prop.PropertyName)
</div>
<div class="editor-field">
#Html.Editor(prop.PropertyName, prop.Model)
#Html.ValidationMessage(prop.PropertyName, "*")
</div>
}
}
}

Categories

Resources