Asp.net MVC multiple select for List property - c#

I'm fairly new to ASP.Net MVC so forgive me for anything that should just be obvious.
I have an object that contains a property that is a list. I only don't know how I should implement this in the create.
this is the object:
public class TeamMember
{
public int TeamMemberId { get; set; }
public string FristName { get; set; }
public string LastName { get; set; }
public DateTime BirthDate { get; set; }
public string Biographie { get; set; }
public virtual Image Image { get; set; }
public virtual List<DanGrade> DanGrades { get; set; }
}
In the create view I want to be able to select multiple Dangrades.
I tried to modify an editor Template for it that looks like this:
#using BudoschoolTonNeuhaus.Models
#model BudoschoolTonNeuhaus.Models.TeamMember
#{
var db = new ApplicationDbContext();
var danGrades = db.DanGrades.ToList();
}
<select multiple name="#ViewData.TemplateInfo.HtmlFieldPrefix" class="dropdown">
#foreach (var dan in danGrades)
{
<option value="#">
#dan.DanGradeId: #dan.BudoSport, #dan.Grade
</option>
}
</select>
but this does not give the result that I thought it would, its just showing mutiple dangrade labels in the create view that you can see here:
#model BudoschoolTonNeuhaus.Models.TeamMember
#{
ViewBag.Title = "Create";
Layout = "~/Views/Shared/_Admin_Layout.cshtml";
}
<div class="wrapper">
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>TeamMember</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.FristName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.FristName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.FristName, "", new { #class = "text-danger" })
</div>
</div>
.... // controls for other properties of model
<div class="form-group">
#Html.LabelFor(model => model.DanGrades, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.DanGrades, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.DanGrades, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Image, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
<input type="file" id="Image" name="Image" hidden />
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
</div>
current HTML output:
Thanks for you help in advance!

To create a <select multiple> you use the ListBoxFor() method in your view.
But your model needs two properties to generate a listbox, a IEnumerable<int> to bind the selected values to (assumes the ID proeprty of DanGrade is typeof int), and an IEnumerable<SelectListItem> to display the <option> elements.
You editing data, so always start with a view model
public class TeamMemberVM
{
public int? TeamMemberId { get; set; }
....
[Display(Name = "DanGrades")]
public IEnumerable<int> SelectedDanGrades { get; set; }
public IEnumerable<SelectListItem> DanGradesList { get; set; }
}
and your view will be
#model yourAssembly.TeamMemberVM
....
#Html.ListBoxFor(m => m.SelectedDanGrades, Model.DanGradesList, new { #class="dropdown" })
and your controller methods will be
public ActionResult Create()
{
TeamMemberVM model = new TeamMemberVM();
ConfigureViewModel(model);
// For an Edit method, your would set the existing selected items here
model.SelectedDanGrades = ...
return View(model);
}
public ActionResult Create(TeamMemberVM model)
{
if (!ModelState.IsValid)
{
ConfigureViewModel(model); // repopulate the SelectList
return View(model);
}
// model.SelectedDanGrades contains the ID's of the selected options
// Initialize an instance of your data model, set its properties based on the view model
// Save and redirect
}
private void ConfigureViewModel(TeamMemberVM model)
{
IEnumerable<DanGrade> danGrades = db.DanGrades();
model.DanGradesList = danGrades.Select(x => new SelectListItem
{
Value = x.DanGradeId.ToString(),
Text = x.??? // the name of the property you want to use for the display text
});
}
Note also that your view has a file input so your view model needs a HttpPostedFileBase property to bind the file to
public HttpPostedFileBase Image { get; set; }
and in the view
#Html.TextBoxFor(m => m.Image, { new type ="file" })

Shouldn't your model be like that ?
[UIHint("NameOfTheEditorTemplate")]
public virtual List<DanGrade> DanGrades { get; set; }
Be sure to put the EditorTemplate under one of these two paths
~/Views/Shared/EditorTemplates
~/Views/Controller_Name/EditorTemplates
As explained in this post

So you are trying to save a list of custom objects inside your object. First of all, know that if you try to save teammember to a database your list of objects will not save. I've experienced this same issue and its needs some special configuring to get just that to work.
Second you can't select custom objects from a < select >. Select returns string[] to your controller. So objects, no. You can't return complex items like that using select directly.
What you can do is return a string[] and use the individual strings (maybe it contains name, maybe it contains id?) and then use that array to pull each object to your teammember object in the controller from the dangrade db context (I'm assuming that is where they are stored).
So for example if you Go back to your controller and add (string[] dangrades) to your parameters. Your parameters now looks something like this (string[] dangrades, Bind[blahblah] ... teammember).
Now after referencing the other database you can do as follows
teammember.Dangrades = new list<Dangrade>();
foreach(string item in dangrades)
{
var dangradeselected = from x in db.dangrades where x.name = item select x;
var dangradefromlinq = dangradeselected.tolist();
teammember.Dangrades.Add(dangradefromlinq[0]);
}
If you had previously stored dangrades in some other format (ie not a database) then you will have to append your code, or ask specifically with that for a better answer.
Also don't forget to give your select and id= (lookup html attributes) so that the controller can recognize it.
You can probably make this (pseudo)code a little neater. Also don't forget about possible null values.
If you want to save a list of items for each teamember you can also look into having 2 databases. I'm not sure if this is recommended. But you can have one for teammembers, and one for dangrades. In the case of dangrades you would add an additional property called grouping id that would match the id of your teammember. So when you pull up your teammember you could also pull up all related dawngrades that match its database id.
That's everything I can think of. If you find a simpler solution by all means go with that.

Related

DropDownList doesn't want to be set

My dropDown list doesn't want to have default value!
<div class="form-group">
#Html.LabelFor(model => model.unit, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(x => x.unit.id, selectUnit)
#Html.ValidationMessageFor(model => model.unit.id, "", new { #class = "text-danger" })
</div>
</div>
Show me the right list but none is selected.
I get my SelectList by using ViewBag:
#{
IEnumerable<SelectListItem> selectUnit = ViewBag.Unit;
}
When I breakpoint the cshtml, Model.unit.id is 4 and selectUnit have one item with 4 as value.
When I do
#selectUnit.Where(x => x.Value == Model.unit.id.ToString()).First().Text
it selects the right text value!
Lats think: this is my Unit model:
public class Unit
{
public int id { get; set; }
public string name { get; set; }
public string description { get; set; }
public IList<Unit> children { get; set; }
}
Thanks in advance folks, I'm becoming crasy
EDIT:
public class ModelPassedTroughTheView
{
...
public Unit unit { get; set; }
}
EDIT 2: Full code:
Edit page:
#model BE.DealerGroupSAP
#{
ViewBag.Title = Resources.Admin.DealerGroup_Edit;
IEnumerable<SelectListItem> selectUnit = ViewBag.Unit;
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>#ViewBag.Title</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.id)
<div class="form-group">
#Html.LabelFor(model => model.unit, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(x => x.unit.id, selectUnit)
#Html.ValidationMessageFor(model => model.unit.id, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="#Resources.Global.Save_Edits" class="btn btn-default" />
</div>
</div>
</div>
}
Model passer trough view:
public class DealerGroupSAP
{
public int id { get; set; }
public Unit unit { get; set; }
}
Unit object:
public class Unit
{
public int id { get; set; }
public string name { get; set; }
public string description { get; set; }
public IList<Unit> children { get; set; }
}
Controller's content:
ViewBag.Unit = GetUnits();
return View(BL.AnomalyBL.GetAllSAPResponsible(id));
The issue is that your model has a property named unit and your also passing the SelectList view a ViewBag property named Unit (the model binding features of MVC are case insensitive.
Change the name of the ViewBag property to (say)
ViewBag.UnitList = GetUnits();
and in the view
#{ IEnumerable<SelectListItem> selectUnit = ViewBag.UnitList }
and the correct option will be selected.
To explain what is happening internally:
The DropDownListFor() method determines the defaultValue (selected item) by first checking values in ModelState (which in your case do not exist), then checking ViewData. Because ViewData contains a key/value pair for Unit, which is IEnumerable<SelectListItem> and does not contain a property id, the defaultValue is nulland the method uses the IEnumerable<SelectListItem> you passed to the view to build the <option> elements, none of which have a Selected = true value, so the first option is selected because something has to be.
Changing the ViewBag property to to (say) UnitList means the method does not find a matching key for unit in ViewData and now inspects the model for unit.id, which exists, and sets defaultValue = 4. Because defaultValue is not null, a new IEnumerable<SelectListItem> is generated internally, and the corresponding SelectListItem has its Selected property set to true.
To understand how this all works in detail, you can inspect the source code for SelectExtensions - in particular the private static MvcHtmlString SelectInternal() method.
As a final note, this is just one more reason why you should always use a view model.

ASP.NET MVC ViewModel Property is null

I have the following ViewModel:
public class ProjectViewModel
{
public Project Project { get; set; }
public Customer Customer { get; set; }
}
The Customer property is only used to link a new Project to the Customer, so I don't include this property in my Create view, which looks like this:
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Project</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Project.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Project.Name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Project.Name, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
When I post the form, the following method in my ProjectsController is triggered:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "Project,Customer")] ProjectViewModel vm)
{
var project = Mapper.Map<Project>(vm);
if (ModelState.IsValid)
{
_db.Create(project);
return RedirectToAction("Index");
}
return View(vm);
}
This is where the unexpected behaviour occurs. Now, when I examine the vm property, the Customer property is null.
The question
How can I still keep the Customer property filled, while not using it in the view?
If you want to persist your Customer data, then you need to set all the fields as hidden elements otherwise they will be lost in the redirect.
#Html.HiddenFor(model => model.Customer.Property1) ...etc...
The short answer is that you can't. When data is posted back, the only data that is included is what is in the form on the HTML page.
Your best bet is to either use a session variable or look up the data again in the post handler, or alternatively, serialize the data to hidden fields.
To make sure the Customer property is never null when initializing ProjectViewModel, you could add a constructor to your ProjectViewModel class initializing the Customer property to a new Customer() like so:
public class ProjectViewModel
{
// Constructor
public ProjectViewModel()
{
Customer = new Customer();
}
public Customer Customer { get; set; }
}

Grouping in for loop razor view

I Have the following in my razor view
#foreach (var group in Model.QuestionList.GroupBy(x => x.AreaName))
{
<h4>#group.Key</h4>
for (int i = 0; i < Model.QuestionList.Count(x => x.AreaName == group.Key); i++)
{
<div class="form-group">
<div class="row">
<div class="col-md-4">
#Html.DisplayFor(x => Model.QuestionList[i].Question)
</div>
<div class="col-md-2">
#Html.HiddenFor(x => Model.QuestionList[i].StnAssureQuestionId)
#Html.DropDownListFor(model => model.QuestionList[i].Score, new SelectList(Model.QuestionList[i].Scores, "ScoreId", "ScoreNum", 0), "Please Select", new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.QuestionList[i].Score, "", new { #class = "text-danger" })
</div>
<div class="col-md-4">
#Html.EditorFor(x => Model.QuestionList[i].Comments, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.QuestionList[i].Comments, "", new { #class = "text-danger" })
</div>
</div>
</div>
}
}
I want to be able to display all the objects in QuestionList but group them by AreaName, which is the group key.
The current code displays the title of the first group then the questions in that group but after that all it does is display the next group name followed by the same questions then again for all the group.
It's a no brainer I'm sure but I'm still not skilled enough to spot it.
You might be able to get by with something like this, but I'd take other's advice about creating a specific view model for this view also.
#foreach (var group in Model.QuestionList.GroupBy(x => x.AreaName))
{
var questionList = group.ToList();
<h4>#group.Key</h4>
for (int i = 0; i < questionList.Count(); i++)
{
<div class="form-group">
<div class="row">
<div class="col-md-4">
#Html.DisplayFor(x => questionList[i].Question)
</div>
<div class="col-md-2">
#Html.HiddenFor(x => questionList[i].StnAssureQuestionId)
#Html.DropDownListFor(model => questionList[i].Score, new SelectList(questionList[i].Scores, "ScoreId", "ScoreNum", questionList[i].Score), "Please Select", new { #class = "form-control" })
#Html.ValidationMessageFor(model => questionList[i].Score, "", new { #class = "text-danger" })
</div>
<div class="col-md-4">
#Html.EditorFor(x => questionList[i].Comments, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => questionList[i].Comments, "", new { #class = "text-danger" })
</div>
</div>
</div>
}
}
Dot Net Fiddle Example
You should not be using complex queries in your view, and while the JamieD77's answer will solve the issue of correctly displaying the items, it will fail to bind to your model when you submit.
If you inspect the html you generating you will see that for each group you have inputs such as
<input type="hidden" name="questionList[0].StnAssureQuestionId" ... />
<input type="hidden" name="questionList[1].StnAssureQuestionId" ... />
but the DefaultModelBinder requires collection indexers to start at zero and be consecutive so when binding, it will correctly bind the inputs in the first group, but ignore those in all other groups because the indexers starts back at zero.
As always start with view models to represent waht you want to display/edit (What is ViewModel in MVC?). In this case I'm assuming the SelectList options associated with Score are common across all Questions
public class QuestionnaireVM
{
public IEnumerable<QuestionGroupVM> Groups { get; set; }
public SelectList Scores { get; set; }
}
public class QuestionGroupVM
{
public string Name { get; set; }
public IEnumerable<QuestionVM> Questions { get; set; }
}
public class QuestionVM
{
public int ID { get; set; }
public string Question { get; set; }
public int Score { get; set; }
public string Comments { get; set; }
}
While you could use nested loops in the view
for (int i = 0; i < Model.Groups.Count; i++)
{
<h4>Model.Groups[i].Name</h4>
for (int j = 0; j < Model.Groups[i].Count; j++)
{
#Html.DisplayFor(m => m.Groups[i].Questions[j].Question)
a better solution is to use EditorTemplates which give you a reusable component (and you would not have to change the collection properties to IList<T>). Note I have omitted <div> elements to keep it simple.
In /Views/Shared/EditorTemplates/QuestionVM.cshtml
#model QuestionVM
#Html.DisplayFor(m => m.Question)
#Html.HiddenFor(m => m.ID)
#Html.DropDownListFor(m => m.Score, (SelectList)ViewData["scores"], "Please Select", new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Score, "", new { #class = "text-danger" })
#Html.EditorFor(m => m.Comments, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(m => m.Comments, "", new { #class = "text-danger" })
In /Views/Shared/EditorTemplates/QuestionGroupVM.cshtml
#model QuestionGroupVM
<h4>#Html.DisplayFor(m => m.Name)</h4>
#Html.EditorFor(m => m.Questions, new { scores = ViewData["scores"] })
and the main view would be
#model QuestionnaireVM
#using (Html.BeginForm())
{
#Html.EditorFor(m => m.Groups, new { scores = Model.Scores })
<input type="submit" value="Save" />
}
Then in the get method, project your data model to the view model, for example
QuestionnaireVM model = new QuestionnaireVM
{
Groups = db.Questions.GroupBy(x => x.AreaName).Select(x => new QuestionGroupVM
{
Name = x.Key,
Questions = x.Select(y => new QuestionVM
{
ID = y.StnAssureQuestionId,
Question = y.Question,
Score = y.Score,
Comments = y.Comments
}
},
Scores = new SelectList(.....)
};
return View(model);
and the signature of the POST method would be
public ActionResult Edit(QuestionnaireVM model)
Side note: You do not currently have an input for the group name property which means if you needed to return the view because ModelState was invalid, you would need to run the query again, so consider adding #Html.HiddenFor(m => m.Name) to the QuestionGroupVM.cshtml template (and of course if you do retur the view, you also need to reassign the SelectList property.
When you displaying your questions you should work with your group object (grouped collection) but not with the initial collection.
I mean you should change your
Model.QuestionList[i]
To
group.Select(x => x.QuestionList)[i]
Anyway your solution really messy It's better to do such grouping on server side.
Please consider that a View should be completely agnostic to the business logic implemented by the service layer. View is just a dummy presentation mechanism which grabs data served by a Controller through a ViewModel and displays the data. It is the basic of the MVC architecture and my strong recommendation is that following the architecture is itself a very good reason to go the right way.
That being said the view you have is getting messy. Consider reconstructing it as something like this:
public class QuestionDataViewModel
{
public List<QuestionData> Data { get; set; }
}
public class QuestionData
{
public string AreaName { get; set; }
public List<Question> QuestionList { get; set; }
}
public class Question
{
public int StnAssureQuestionId { get; set; }
public int Score { get; set; }
public IEnumerable<SelectListItem> Scores { get; set; }
public List<Comment> Comments { get; set; }
}
Construct this server side and just render through simple razor foreach loops. This will not only benefit you with cleaner code but will also help avoid the model binding pain you are about to run into when you post the form back with your current implementation.

MVC5 view drop down list from ViewBag

I'm new to MVC5/C# (fresh off a Silverlight project) and have a web application (not ASP.net) that I'm working on. I can't figure out how to get the value from a dropdown list that is populated from a ViewBag and not the model. Everything I've seen is geared towards ASP.NET and/or populating the dropdown from the model.
I have this model for shifts:
public class Shift
{
public Guid ShiftID { get; set; }
public string AreaOfOperation { get; set; }
public string UserName { get; set; }
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
}
And this for AreaOfOperations:
public class AreaOfOperations
{
public Guid AreaOfOperationsID { get; set; }
public String AreaOfOperation { get; set; }
public bool InUse { get; set; }
}
The relevant controller code, which populates the view nicely with a working dropdown:
public ActionResult Create(DateTime? datetime)
{
List<AreaOfOperations> list = db.AreaOfOperations.Where(i => i.InUse == true).OrderBy(aoo => aoo.AreaOfOperation).ToList();
ViewBag.DropDownAOOs = new SelectList(list, "AreaOfOperationsID", "AreaOfOperation");
Shift shift = new Shift();
shift.ShiftID = Guid.NewGuid();
shift.StartTime = DateTime.Now;
shift.UserName = User.Identity.Name;
return View(shift);
}
// POST: Shifts/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "ShiftID,AreaOfOperations,UserName,StartTime")] Shift shift)
{
try
{
if (ModelState.IsValid)
{
shift.ShiftID = Guid.NewGuid();
db.Shifts.Add(shift);
db.SaveChanges();
return RedirectToAction("Index");
}
}
catch (DataException /* dex */)
{
//Log the error (uncomment dex variable name and add a line here to write a log.
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists see your system administrator.");
}
return View(shift);
}
And my view:
#model CRMgr5.Models.Shift
#{
ViewBag.Title = "Start Shift";
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Shift</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.AreaOfOperations, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("AreaOfOperation", ViewBag.DropDownAOOs as SelectList, new { htmlAttributes = new { #class = "form-control" } })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.UserName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.UserName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.UserName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.StartTime, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.StartTime, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.StartTime, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input id="btnStartShift" type="submit" value="Start Shift" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Any help would be greatly appreciated. Thanks.
In the drop down list you named your select as "AreaOfOperation" but the model property is called "AreaOfOperations." Hence the binder will not be able to bind it.
As someone here already suggested you should use strongly typed html helpers such as DropDownListFor:
#Html.DropDownListFor(m => m.AreaOfOperations, ViewBag.DropDownAOOs as SelectList)
You did it for the label not sure why you opted not to use it when generating a drop down list?
I just recreated the whole thing and it worked fine
I removed the s of AreaOfOperations in your Bind Attribute
[Bind(Include = "ShiftID,AreaOfOperation(s),UserName,StartTime")]
As far as i know, you can remove this parameter attribute alltogether.
This is only used when you only want to bind to certain Attributes of your view model.
However there was one mistake: you have to repopulate the Select List if your ModelState is not valid. Otherwise your
return View(shift);
does not have the data to render a new SelectList.
Another approach is that you put the data in your ViewModel and initialize it in the default constructor. Then you dont have to worry about the data or casting.

Filter model based on checked checkboxes

I have a simple EntityFramework model that I'm trying to filter by using checkboxes.
My model contains 2 properties:
public class Company
{
public string Name { get; set; }
public string Location { get; set; }
}
What I'd like to do is to:
Show an index view of all the companies in the database
Show a list of checkboxes corresponding to each of the distinct locations of the companies
Allow a user to select one or more of the checkboxes and submit the form to redisplay the index with the filter to locations applied.
I'm not sure how best to approach this - I think I need a viewmodel of my Companies and locations, but I'm not sure how to display the list of checkboxes and then filter by them.
Just add an IsSelected property along with your Company and Location properties. Your checkboxes should be wired to those. Then when a user submits the form, your model will pick up these values. You can just filter your data based on IsSelected being true and return the matching rows.
Edit to show an example. Quickly tested with IE since it's such an aggressive cache monster:
A quick note. It's late and I'm tired. Take the concept but think more clearly about your objects and storage. This is a working example only.
First, a ViewModel:
public class CompanyModel
{
public string Name { get; set; }
public string Location { get; set; }
public bool IsSelected { get; set; }
}
And the Get and Post actions in the controller:
static IList<CompanyModel> CompanyList = null;
public ActionResult Company()
{
if (CompanyList == null)
{
CompanyList = new List<CompanyModel>()
{
new CompanyModel() { Name = "Company 1", Location = "Boston" },
new CompanyModel() { Name = "Company 2", Location = "New York" },
new CompanyModel() { Name = "Company 3", Location = "Chicago" },
new CompanyModel() { Name = "Company 4", Location = "Austin" }
};
}
return View(CompanyList);
}
[HttpPost]
public ActionResult Company(CompanyModel[] companies)
{
CompanyList = companies.Where(c => c.IsSelected).ToList();
return RedirectToAction("Company");
}
This demonstrates the Post, Redirect, Get pattern to ensure the browser doesn't display incorrect results from caching and won't lead to undesired behavior if the user refreshes the screen which would re-submit the original form.
Now the view using the scaffold code for an Edit screen:
#model IList<MVCEditorTemplateDemo.Models.CompanyModel>
#{
ViewBag.Title = "Company";
}
<h2>Company</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>CompanyModel</h4>
<hr />
#Html.ValidationSummary(true)
#for (int i = 0; i < Model.Count(); i++)
{
<div class="form-group">
#Html.LabelFor(model => model[i].Name, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model[i].Name)
#Html.ValidationMessageFor(model => model[i].Name)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model[i].Location, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model[i].Location)
#Html.ValidationMessageFor(model => model[i].Location)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model[i].IsSelected, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model[i].IsSelected)
#Html.ValidationMessageFor(model => model[i].IsSelected)
</div>
</div>
}
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>

Categories

Resources