Dynamically adding controls in MVC4 - asp.net-mvc

I am currently working on creating an MVC4 application where I want controls to be generated automatically from the database rows.
I have the table in my database containing the questions and the control type in which it should be answered by the user.
I am just thinking of a logic like
Where I can get the database rows in a dataset and then foreach it, then checking the type of control it belongs and then creating the control in my View.
This is my Controller action:
Public ActionResult Index()
{
// get the rows from the table
foreach(Iterate the rows)
{
if(controllerType1)
{
//stmnts
}
if(controllerType2)
{
//stmnts
}
}
return View();
}
This is just an idea of how can we build the solution. If I am going in the right way please guide me, else I am eager to know the possibilities where I can build my solution in different ways :).

You can create a editor template and pass the control list as model to the template and in the template you can iterate that list to generate the control. As i have shown below.
1->Create a class for Control Information.
public class ControlInfo
{
public string ControlType { get; set; }
public string ControlID { get; set; }
public string ControlName { get; set; }
public string ControlValue { get; set; }
public string ControlLabel { get; set; }
public bool IsChecked { get; set; }
}
2->Create an Editor Template (Partial view with control name say CustomControl.cshtml) in \Views\Shared\EditorTemplates path.
#model List<MvcApplication2.Models.ControlInfo>
<table>
#foreach (MvcApplication2.Models.ControlInfo ControlInfo in Model)
{
<tr>
<td>#ControlInfo.ControlLabel</td>
<td>
#switch (ControlInfo.ControlType.ToLower())
{
case "textbox":
<input type="text" name="#ControlInfo.ControlName" id="#ControlInfo.ControlID" value="#ControlInfo.ControlValue" />
break;
case "checkbox":
if (ControlInfo.IsChecked)
{
<input type="checkbox" name="#ControlInfo.ControlName" id="#ControlInfo.ControlID" value="#ControlInfo.ControlValue" checked="checked" />
}
else
{
<input type="checkbox" name="#ControlInfo.ControlName" id="#ControlInfo.ControlID" value="#ControlInfo.ControlValue" checked="checked" />
}
break;
default:
break;
}
</td>
</tr>
}
</table>
3->Create a model for main view (say HomeModel).
public class HomeModel
{
public List<ControlInfo> ControlList { get; set; }
public void PolulateControlList()
{
//You can fill this list from database.
// For example i have filled the list manually.
ControlList = new List<ControlInfo>();
ControlList.Add(new ControlInfo() {ControlType="TextBox",ControlName="tbox1", ControlID="tbox1", ControlLabel="Name", ControlValue="Martin" });
ControlList.Add(new ControlInfo() { ControlType = "CheckBox", ControlName = "cbox1", ControlID = "cbox1", ControlLabel="Is Correct", ControlValue = "Yes", IsChecked=true });
}
}
4->Consume the editor template in the main view as.
#model MvcApplication2.Models.HomeModel
#{
ViewBag.Title = "Home Page";
}
#Html.EditorFor(model=>model.ControlList,"CustomControl")
5-> Call the main view in the controller ( Index here).
public ActionResult Index()
{
HomeModel ModelObj = new HomeModel();
ModelObj.PolulateControlList();
return View(ModelObj);
}
Edit 1:
For getting the posted value you need to modify the Editor Templates as below. The model properties whose name is equal to the name of the control posted as name value collection , will get automatically binded by the model binder of the mvc frame work, so for each property in the control collection i have created hidden tags and one input tag for the input value.
#model List<MvcApplication2.Models.ControlInfo>
<table>
#{ var index = -1;}
#foreach (MvcApplication2.Models.ControlInfo ControlInfo in Model)
{
index++;
<tr>
<td>#ControlInfo.ControlLabel</td>
<td>
<input type="hidden" name="#("ControlList[" + index + "].ControlID")" value="#ControlInfo.ControlID" />
<input type="hidden" name="#("ControlList[" + index + "].ControlLabel")" value="#ControlInfo.ControlLabel" />
<input type="hidden" name="#("ControlList[" + index + "].ControlName")" value="#ControlInfo.ControlName" />
<input type="hidden" name="#("ControlList[" + index + "].ControlType")" value="#ControlInfo.ControlType" />
#switch (ControlInfo.ControlType.ToLower())
{
case "textbox":
<input type="text" name="#("ControlList["+index+"].ControlValue")" id="#ControlInfo.ControlID" value="#ControlInfo.ControlValue" />
break;
case "checkbox":
<input type="hidden" name="#("ControlList[" + index + "].ControlValue")" value="#ControlInfo.ControlValue" />
if (ControlInfo.IsChecked)
{
<input type="checkbox" name="#("ControlList[" + index + "].IsChecked")" id="#ControlInfo.ControlID" value="true" checked="checked" />
}
else
{
<input type="checkbox" name="#("ControlList[" + index + "].IsChecked")" id="#ControlInfo.ControlID" value="true" />
}
break;
default:
break;
}
</td>
</tr>
}
</table>
And in the main view you need to have form
#model MvcApplication2.Models.HomeModel
#{
ViewBag.Title = "Home Page";
}
#using(Html.BeginForm()){
#Html.EditorFor(model=>model.ControlList,"CustomControl")
<input type="submit" name="name" value="Submit" />
}
And in controller you need to have corresponding post method
[HttpPost]
public ActionResult Index(HomeModel ModelObj)
{
// Your logic..........
return View(ModelObj);
}
This ModelObj will have the posted values.

Related

SelectediAlertIndex from SelectTagHelper (apparently) not passed to Index.cshtml from LoadAlert(…) …RedirectToAction(“Index”,”Alerts”)

Struggling with getting the SelectedAlertIndex (int) from AspNetCore MVC view page:
#{
<h4>#Model.Alerts.Count Alerts</h4>
<form asp-controller="Alerts" asp-action="LoadAlert" method="post">
<select asp-for="SelectedAlertIndex" asp-items="#Model.Alert_Identifiers">
<option>Select one</option>
</select>
<br />
<input type="submit" name="LoadAlert" value="LoadAlert" />
</form>
}
thru AlertsController.cs:
[HttpPost]
public IActionResult LoadAlert(Alert obj, string LoadAlert)
{
if (!string.IsNullOrEmpty(LoadAlert))
{
ViewBag.Message = "Alert loaded successfully";
}
return RedirectToAction("Index", "Alerts")
}
to #foreach loop inside table in Views/Alerts/Index.cshtml:
#model IEnumerable<edxl_cap_v1_2.Models.ContentViewModels.Alert>
…
<table id="elementTable" class="smallText">
#foreach (var item in Model)
{
#*#while(item.AlertIndex == item.SelectedAlertIndex)
{*#
<tr>
Without the nested #while loop, the table of rows, one for each data element, displays all three records of 13 data elements each, but what I want is to display the set of 13 data elements for the one selected record using the int value of the AlertIndex selected from the Select Tag Helper dropdownlist. If I uncomment the #while loop only the border of the “elementTable” is displayed with no rows. This also happens when I try adding a “Where” clause to the #foreach loop:
#foreach (var item in Model.Where(item => item.AlertIndex == item.SelectedAlertIndex))
{
I’m trying to only show relevant code for the problem, but just in case it’s needed here’s the basic model:
public class AlertViewModel
{
public int SelectedAlertIndex { get; set; }
public List<SelectListItem> Alert_Identifiers { get; set; }
public List<AlertVm> Alerts { get; set; }
}
public class AlertVm
{
[Key]
public int AlertIndex { get; set; }
[MaxLength(150)]
public string Alert_Identifier { get; set; }
}
I decided it probably wasn’t needed to show the Alert class of 13 elements that I ended up adding to the view model, from the original Alert.cs class.
I needed to change value of asp-action in the form tag helper usage to Index:
#{
<h4>#Model.Alerts.Count Alerts</h4>
<form asp-controller="Alerts" asp-action="Index" method="post">
<select asp-for="SelectedAlertIndex" asp-items="#Model.Alert_Identifiers">
<option>Select one</option>
</select>
<br />
<input type="submit" name="Index" value="LoadAlert" />
</form>
}
Then I needed to make sure that SelectedAlertIndex was spelled the same in all instances; changed its type to Nullable int (int?); put a null check in the HttpPost Index() action; and add a Where condition:
[HttpPost]
public IActionResult Index(Alert obj, int? SelectedAlertIndex)
{
if (SelectedAlertIndex.HasValue)
{
ViewBag.Message = "Alert loaded successfully";
}
return View(_context.Alert.Where(x => x.AlertIndex == SelectedAlertIndex));
}
Many Thanks to #Shyju

Passing values of checkboxes from View to Controller

I have a view with a number of checkboxes in it. I want to be able to pass the values of the checkboxes to the controller, then output a list of the OfficeNames that have been ticked. I am not sure how to pass the values of multiple checkboxes back to the controller, or how to output the OfficeNames based on which boxes have been ticked
View:
<p>
#using (Html.BeginForm())
{
<p>
Start Date: #Html.TextBox("StartDate") <br />
<br />
End Date: #Html.TextBox("EndDate") <br />
<br />
<input type="submit" value="Filter" />
</p>
}
<p>
#foreach (var item in Model.BettingOffices)
{
<label>#Html.DisplayFor(modelItem => item.OfficeName)</label>
<input type="checkbox" name="selectedShops" value="#item.OfficeName">
}
</p>
Controller:
public class DailyReportController : Controller
{
private RiskEntities _db = new RiskEntities();
// GET: /DailyReport/
public ActionResult Index(DateTime? startDate, DateTime? endDate)
{
if (startDate == null || endDate == null)
{
var dailyReportModelBlank = new DailyReportModel();
dailyReportModelBlank.BettingOffices = (from bo in _db.BettingOffices orderby bo.OfficeName select bo ).ToList();
//dailyReportModelBlank.DailyReports.Add(new DailyReport());
return View(dailyReportModelBlank);
}
var endDateToUse = (DateTime) endDate;
endDateToUse = endDateToUse.AddDays(+1);
var dailyReportModel = new DailyReportModel
{
DailyReports = (from dr in _db.DailyReports
where dr.DailyReportDate >= startDate
&& dr.DailyReportDate <= endDateToUse
select dr).ToList(),
BettingOffices = (from bo in _db.BettingOffices select bo).ToList()
};
return View(dailyReportModel);
}
Model:
public class DailyReportModel
{
private List<DailyReport> _dailyReports = new List<DailyReport>();
private List<BettingOffice> _bettingOffices = new List<BettingOffice>();
public List<DailyReport> DailyReports
{
get { return _dailyReports; }
set { _dailyReports = value; }
}
public List<BettingOffice> BettingOffices
{
get { return _bettingOffices; }
set { _bettingOffices = value; }
}
}
BettingOffice Class:
public partial class BettingOffice
{
public int BettingOfficeID { get; set; }
public string OfficeName { get; set; }
public string OfficeCode { get; set; }
public string IpAddress { get; set; }
public Nullable<bool> SupportOnly { get; set; }
public Nullable<int> SisSrNumer { get; set; }
public Nullable<bool> Local { get; set; }
public string Server { get; set; }
}
try this :
<p>
#using (Html.BeginForm())
{
<p>
Start Date: #Html.TextBox("StartDate")
<br />
<br />
End Date: #Html.TextBox("EndDate")
<br />
<br />
<input type="submit" value="Filter" />
</p>
}
</p>
<p>
#foreach (var item in Model.BettingOffices)
{
<label>#Html.DisplayFor(modelItem => item.OfficeName)</label>
<input type="checkbox" name="bettingOfficeIDs" value="#item.BettingOfficeID">
}
</p>
And in your Action you can get the selected office ids in bettingOfficeIDs variable:
public ActionResult YourActionName(int[] bettingOfficeIDs)
Few things that need to change here.
If you want values to be passed to action method they need to be within form not outside
For MVT to 'understand' checkbox values as array (or more complex object) you need to work with their html name attribute.
I will do demonstration application below that should help you understand how it works:
CsHtml: Notice that you need to add value attribute to checkboxes to be able to read their values, checkbox gets true only when checkbox is ticked and value is true, hence the javascript. You can add as many of complex object properties as hidden fields as long as you give them names that match to the object property names in viewModel. In this case I am only passing BettingOfficeID
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
$(document).on("click", "[type='checkbox']", function(e) {
if (this.checked) {
$(this).attr("value", "true");
} else {
$(this).attr("value","false");}
});
<p>
#using (Html.BeginForm())
{
<p>
Start Date: #Html.TextBox("StartDate") <br />
<br />
End Date: #Html.TextBox("EndDate") <br />
<br />
</p>
<p>
<input type="checkbox" name="BettingOffices[0].Selected" value="true">
<input type="hidden" name="BettingOffices[0].BettingOfficeID" value="1">
<input type="checkbox" name="BettingOffices[1].Selected" value="false">
<input type="hidden" name="BettingOffices[1].BettingOfficeID" value="2">
<input type="checkbox" name="BettingOffices[2].Selected" value="true">
<input type="hidden" name="BettingOffices[2].BettingOfficeID" value="3">
<input type="checkbox" name="BettingOffices[3].Selected" value="false">
<input type="hidden" name="BettingOffices[3].BettingOfficeID" value="4">
<input type="checkbox" name="BettingOffices[4].Selected" value="true">
<input type="hidden" name="BettingOffices[4].BettingOfficeID" value="5">
</p>
<input type="submit" value="submit"/>
}
Post Action method to add to controller
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Index(BettingViewModel viewModel)
{
return null;
}
BettingViewModel: I have added Selected property to BettingOffice class.
public class BettingViewModel
{
public string StartDate { get; set; }
public string EndDate { get; set; }
public List<BettingOffice> BettingOffices { get; set; }
}
public class BettingOffice
{
public bool Selected { get; set; }
public int BettingOfficeID { get; set; }
public string OfficeName { get; set; }
public string OfficeCode { get; set; }
public string IpAddress { get; set; }
public Nullable<bool> SupportOnly { get; set; }
public Nullable<int> SisSrNumer { get; set; }
public Nullable<bool> Local { get; set; }
public string Server { get; set; }
}
Hope this saves you some time.
View:
#using (Html.BeginForm("Createuser", "User", FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
#Html.AntiForgeryToken()
<h4>Create a new account.</h4>
<div class="form-group">
#Html.LabelFor(m => m.city, new { #class = "col-md-2 control-label" })
</div>
<div class="col-md-10">
<table>
<tr>
<td><input type="checkbox" name="city" value="Pune" id="1" />Pune</td>
<td><input type="checkbox" name="city" value="Banglore" id="2" />Banglore</td>
<td><input type="checkbox" name="city" value="Mumbai" id="3" />Mumbai</td>
</tr>
</table>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-default" value="Create" />
</div>
</div>
}
[HttpPost]
public ActionResult Createuser(user user, string [] city)
{
var UserInfo = new user
{ Email =user.Email,Password=user.Password,Firstname=user.Firstname };
return View();
}
1. First of all, you are generating checkboxes with same name. So how you will be able to retrieve them on server end separately?
So declare some counter that gets incremented and name checkboxes uniquely.
#foreach (var item in Model.BettingOffices)
{
int counter=1;
var checkboxName = "selectedShops" + counter;
<label>#Html.DisplayFor(modelItem => item.OfficeName)</label>
<input type="checkbox" name="#checkboxName" value="#item.OfficeName">
counter++;
}
2. Now on submission of Form in your controller, get checkboxes as -
//Loop through the request.forms
for (var i = 0; i <= Request.Form.Count; i++)
{
var checkboxValue = Request.Form["selectedShops[" + i + "]"];
// Do whatever you want to with this checkbox value
}
For ticked values, you will probably get True value. Debug the retrieved value to write further code accordingly.
Try the following
your View is:
#foreach (var item in Model.BettingOffices)
{
<label>#Html.DisplayFor(modelItem => item.OfficeName)</label>
<input type="checkbox" name="selectedShops" value="#item.OfficeName">
}
Controller
[HttpPost]
public ActionResult Index(FormCollection collection)
{
if(!string.IsNullOrEmpty(collection["selectedShops"]))
{
string strSelectedShops = collection["selectedShops"];
}
}
Hi you can get the selected checkbox value using the bellow code it seem working fine fore me,
<script>
$(document).ready(function()
{
$("input[type=checkbox]").click(function()
{
var categoryVals = [];
categoryVals.push('');
$('#Category_category :checked').each(function() {
categoryVals.push($(this).val());
});
$.ajax({
type:"POST",
url:"<?php echo $this->createUrl('ads/searchresult'); ?>", //url of the action page
data:{'category': categoryVals},
success : function(response){
//code to do somethng if its success
}
});
}
}
</script>

Post Model back to Server

My aim is to export the clicked table, to excel (csv). This requires taking the Model on the page, and posting it to the server. But its providing difficult as its a list.
How do I post the Model in the View, to the controller.
Below is my code
View:
#model List<NameSpace.Property>
#using (Html.BeginForm("Export","Excel", FormMethod.Post))
{
<input type="submit" class="submit" value="Submit" />
}
<table .... />
Controller:
public void Export(List<Property> list)
{
}
As you did not state whether your table displays static data or dynamic data I have provided both solutions:
STATIC:
For example, if you request a url (lets say): /home/index/1. The table data is retrieved for that id (of 1) and then displayed in a table in the view.
If the information in the table does not change then you do not need to post any information back to the server. You should perform a GET request rather than a POST request to retrieve the CSV.
You would need to change the view to accept a model containing the original id that was requested and to use an action link rather than a form:
#model MyTableViewModel
#Html.ActionLink("Click here to export", "Export", "Excel", new { id = Model.Id });
<table .... />
Where the view model might be:
public class MyTableViewModel
{
public int Id { get; set; }
public List<Customer> Customers { get; set; }
}
public class Customer
{
public string Name { get; set; }
public string Street { get; set; }
public string PostCode { get; set; }
}
Then your export controller action would be:
public FileContentResult Export(int id)
{
string csv = //Retrieve the information for the provided id and convert to csv format
return File(new System.Text.UTF8Encoding().GetBytes(csv), "text/csv", "expostfilename.csv");
}
DYNAMIC:
The view model would no longer need the id so would become:
public class MyTableViewModel
{
public List<Customer> Customers { get; set; }
}
The view could be written:
#model MyTableViewModel
#using (Html.BeginForm("Export", "Home"))
{
<table>
<tbody>
#for (int i = 0; i < Model.Customers.Count; i++)
{
<tr>
<td>Name: #Html.TextBoxFor(m => m.Customers[i].Name)</td>
<td>Street: #Html.TextBoxFor(m => m.Customers[i].Street)</td>
<td>PostCode: #Html.TextBoxFor(m => m.Customers[i].PostCode)</td>
</tr>
}
</tbody>
</table>
<input type="submit" value="submit" />
}
And the controller export action:
public FileContentResult Export(MyTableViewModel vm)
{
string csv = //convert view model (vm) to csv
return File(new System.Text.UTF8Encoding().GetBytes(csv), "text/csv", "expostfilename.csv");
}
Controller Action
public void Export(List<Property> list)
{
}
Property Class
public class Property
{
public string Name { get; set; }
public string Street { get; set; }
public string Postcode { get; set; }
}
Html Table
<table id="my-table">
<tbody>
<tr>
<td>Chris</td>
<td>Awesome Street</td>
<td>DH9 4LD</td>
</tr>
<tr>
<td>Sean</td>
<td>Lame Street</td>
<td>DH8 4SR</td>
</tr>
</tbody>
</table>
Jquery
<script type="text/javascript">
var aData = {};
$('#my-table tbody tr').each(function(index, value) {
aData['list[' + index + '].Name'] = $(value).children().eq(0).text();
aData['list[' + index + '].Street'] = $(value).children().eq(1).text();
aData['list[' + index + '].Postcode'] = $(value).children().eq(2).text();
});
$.post("#Url.Action("Export")", aData, function(data) {
});
</script>
This should be enough to get you started. Hope this helps
Update
Alternatively if you did not want a Jquery specific solution, you could output your data in hidden fields
<input type="hidden" name="[0].Name" value="Chris" />
<input type="hidden" name="[0].Street" value="..." />
<input type="hidden" name="[0].Postcode" value="...." />
<input type="hidden" name="[1].Name" value="Sean" />
<input type="hidden" name="[1].Street" value="..." />
<input type="hidden" name="[1].Postcode" value="...." />
and this would get submitted in your form

Passing Values from the SelectList Items Using ViewModel

I'd like to get the values of the selected items in dropdownlists. I am saving the files into the database with the following code:
public ActionResult UploadDoc(IEnumerable<HttpPostedFileBase> files)
{
foreach (var file in files)
{
if (file != null && file.ContentLength > 0)
{
byte[] data = new byte[file.ContentLength];
file.InputStream.Read(data, 0, file.ContentLength);
Document doc = new Document
{
UploadedOn = DateTime.Now,
MimeType = file.ContentType,
UserName = User.Identity.Name,
Data = data,
FromLanguage = 1,
ToLanguage = 2
};
dbContext = new MedicalDb();
dbContext.Documents.Add(doc);
dbContext.SaveChanges();
}
}
return RedirectToAction("Index");
}
but, I'd also like to get the selected values from the dropdownlists so that I can populate the FromLanguage and ToLanguage properties of the documents. I guess I'd need a viewmodel, but don't know how to do it. New rows for document upload are added using jQuery and names of the ddls are "ddlFromLanguage1", "ddlFromLanguage2", "ddFromLanguage3", and "ddlToLanguage1", "ddlToLanguage2", "ddlToLanguage3", etc. Thanks in advance for any help.
<form action="UploadDoc" method="post" enctype="multipart/form-data">
<table id="tblUploadDocs">
<tr id="row1">
<td><input type="file" name="files" id="file1" /></td>
<td>Bu dilden</td>
<td>#Html.DropDownList("ddlFromLanguage1", ViewBag.Languages as SelectList)</td>
<td>şu dile çevrilecek</td>
<td>#Html.DropDownList("ddlToLanguage1", ViewBag.Languages as SelectList)</td>
</tr>
</table>
<br />
Yeni dosya ekleyin
<input type="submit" />
</form>
Any form that is posted back returns a FormCollection to the controller in addition to model related values.
For example
//In your view
#using (Html.BeginForm("CountrySelect", "Country", FormMethod.Post))
{
#Html.AntiForgeryToken()
<select name="country" id="country-select">
<option value="selector">Pick a Country</option>
<option value="England">England</option>
<option value="England">England</option>
</select>
}
//In controller
//This will get you the name of the selected country from your form
[HttpPost]
Public ActionResult CountrySelect(FormCollection formData)
{
string country = formData["country"].toString();
}
I think you need to look at good example and do the same or very similar to them.
Take a look at these:
ASP.NET MVC 3 Viewmodel Pattern
Implementing Dropdownlist on Asp.net MVC 3 from viewModel
These should get you going.
Please let me know if you don't succeed or if what I gave you was actually helpful.
Thanks
The solution:
The viewmodel:
public class CustomerDocUploadViewModel
{
public HttpPostedFileBase File { get; set; }
public int FromLanguage { get; set; }
public int ToLanguage { get; set; }
}
The view:
#model IList<Models.ViewModels.CustomerDocUploadViewModel>
...
<form action="UploadDoc" method="post" enctype="multipart/form-data">
<table id="tblUploadDocs">
<tr id="row1">
<td><input type="file" name="[0].File" /></td>
<td>Bu dilden</td>
<td>#Html.DropDownList("[0].FromLanguage", ViewBag.Languages as SelectList)</td>
<td>şu dile çevrilecek</td>
<td>#Html.DropDownList("[0].ToLanguage", ViewBag.Languages as SelectList)</td>
</tr>
</table>
<br />
<a id="lnkAdd" href="javascript:addRow();" style="margin:10px 0;">Yeni dosya ekleyin</a>
<input type="submit" />
</form>
and finally the action method in the controller:
[HttpPost]
public ActionResult UploadDoc(IList<CustomerDocUploadViewModel> docInfos)
{
for (int i = 0; i < docInfos.Count; i++)
{
if (docInfos.ElementAt(i).File != null && docInfos.ElementAt(i).File.ContentLength > 0)
{
byte[] data = new byte[docInfos.ElementAt(i).File.ContentLength];
docInfos.ElementAt(i).File.InputStream.Read(data, 0, docInfos.ElementAt(i).File.ContentLength);
// Save the file into the database
Document doc = new Document
{
UploadedOn = DateTime.Now,
MimeType = docInfos.ElementAt(i).File.ContentType,
UserName = User.Identity.Name,
Data = data,
FromLanguage = docInfos.ElementAt(i).FromLanguage,
ToLanguage = docInfos.ElementAt(i).ToLanguage
};
dbContext = new MedicalDb();
dbContext.Documents.Add(doc);
dbContext.SaveChanges();
}
}
return RedirectToAction("Index");
}

MVC Radio Button Lists are not grouped when using the HtmlHelper class

Having trouble creating a list of radio buttons that are grouped together, in MVC 3 specifically, but this also applies to MVC 2.
The problem arises when radio buttons are generated using Html helpers and the model is part of an array.
Here is the cut down version of my code.
public class CollectionOfStuff {
public MVCModel[] Things { get; set }
}
/*This model is larger and represents a Person*/
public class MVCModel {
[UIHint("Hidden")]
public string Id { get; set; }
public string Name { get; set; }
public bool IsSelected { get; set; }
}
/*Assigned to new CollectionOfStuff property Things*/
var items = new[] {
new MVCModel() { Id="0" Name = "Name here" }, new MVCModel() { Id="1" Name = "Name there" }
}
My parent view
#model CollectionOfStuff
#for (int i = 0; i < Model.Things.Length; i++) {
#Html.EditorFor(m => m.Things[i]);
}
My view rendering individual MVCModel objects
#Model MVCModel
#{
var attr = new {
Checked = Model.IsSelected ? "checked=checked" : ""
};
}
#Html.RadioButtonFor(model => model, Model.Id, attr)
Produces this output:
<input type="radio" value="0" name="MVCModel[0]" id="MVCModel_0_" data-val-required="You need to choose" data-val="true" />
<input type="radio" value="1" name="MVCModel[1]" id="MVCModel_1_" data-val-required="You need to choose" data-val="true" />
The radio buttons are not grouped, however it has the obvious advantage of writing out the meta data for validation.
The other way is by calling:
#Html.RadioButton(name: "GroupName", value: Model.Id, isChecked: Model.IsSelected)
Produces:
<input type="radio" value="0" name="MVCModel[0].GroupName" id="MVCModel_0__GroupName">
<input type="radio" value="1" name="MVCModel[1].GroupName" id="MVCModel_1__GroupName">
Again, this doesn't produce the desired result. It's also missing the validation meta data.
Another other option is creating a custom template, but the problem with this approach is that all the meta data required for validation is not present.
Any ideas on how I can create grouped radio buttons or obtain meta data so I can create a template myself?
You haven't shown how does your view model look like but you could group them by some property. So let's take an example:
public class MyViewModel
{
[Required]
public string SomeProperty { get; set; }
}
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new MyViewModel());
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
return View(model);
}
}
View:
#model AppName.Models.MyViewModel
#using (Html.BeginForm())
{
<div>A: #Html.RadioButtonFor(x => x.SomeProperty, "a")</div>
<div>B: #Html.RadioButtonFor(x => x.SomeProperty, "b")</div>
#Html.ValidationMessageFor(x => x.SomeProperty)
<input type="submit" value="OK" />
}
Now if you want to preselect some radio simply set the property of the view model to the corresponding value of the radio instead of writing some ugly C# code in your views:
public ActionResult Index()
{
var model = new MyViewModel
{
SomeProperty = "a" // select the first radio
};
return View(model);
}
Obviously this technique works with any simple property type (not only strings) and with any number of radio buttons that could be associated to this property.

Resources