LINQ To Entities SaveChanges() not working - asp.net-mvc

I have a scenario where I have a form where I would like administrators to modify their exam / survey details, I am trying to update my database table with the following code.
However the code does not save my changes for the "if" part in my controller, and does not throw any error, and will just redirect me to the next page which is "EditExam2".
I am trying to update the "InformationSheetText" and "InformationConsentForm" fields.
I know the query works as the the "else" part of my code in my controller works when adding a new row into the database.
My View
#model
AppXamApplication.Models
InformationSheetViewModel
#{
ViewBag.Title = "InformationSheet";
}
<!DOCTYPE html>
<html>
<body>
<h2>InformationSheet</h2>
<h3>Survey ID: #ViewBag.CurrentExamID</h3>
#using (Html.BeginForm("InformationSheet", "ExamAdmin", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
<h4>Create Information and Consent Sheet.</h4>
<hr />
#Html.ValidationSummary("", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(m => m.ImageURL, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
<input type="file" name="ImageFile" />
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.InformationSheetText, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.EditorFor(m => m.InformationSheetText, new { #class = "form-control", #rows = 4, #style = "resize: none;" })
</div>
</div>
<div class="form-group">
<div class="col-md-10">
#Html.CheckBoxFor(m => m.Check_InformationSheet, new { #disabled = "disabled", #checked = true })
#Html.LabelFor(m => m.Check_InformationSheet, new { })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.InformationConsentForm, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.EditorFor(m => m.InformationConsentForm, new { #class = "form-control", #rows = 4, #style = "resize: none;" })
</div>
</div>
<div class="form-group">
<div class="col-md-10">
#Html.CheckBoxFor(m => m.Check_InformationConsentForm1, new { #disabled = "disabled", #checked = true })
#Html.LabelFor(m => m.Check_InformationConsentForm1, new { })
</div>
<div class="col-md-10">
#Html.CheckBoxFor(m => m.Check_InformationConsentForm2, new { #disabled = "disabled", #checked = true })
#Html.LabelFor(m => m.Check_InformationConsentForm2, new { })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-default" value="Create Exam" />
</div>
</div>
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
My model
public class InformationSheetViewModel
{
public string ExamID { get; set; }
[Display(Name = "Choose Image To Display")]
public string ImageURL { get; set; }
[Display(Name = "Enter your Information Sheet")]
public string InformationSheetText { get; set; }
[Display(Name = "Enter your Consent Form")]
public string InformationConsentForm { get; set; }
public HttpPostedFileBase ImageFile { get; set; }
[Display(Name = "I had read and understood the information sheet")]
public bool Check_InformationSheet { get; set; }
[Display(Name = "I consent and agree to the information consent form")]
public bool Check_InformationConsentForm1 { get; set; }
[Display(Name = "I have read, agree and consent to the information and conditions")]
public bool Check_InformationConsentForm2 { get; set; }
}
My Controller
[HttpGet]
public ActionResult InformationSheet(string id)
{
if (ModelState.IsValid)
{
ViewBag.CurrentExamID = id;
using (var ctx = new AppXamApplicationEntities())
{
var query = ctx.InformationConsentAndSheets.Where(x => x.ExamID.Equals(id)).Select(x => new InformationSheetViewModel()
{
ExamID = id,
InformationSheetText = x.InformationSheetText,
InformationConsentForm = x.InformationSheetText
}).FirstOrDefault();
return View(query);
}
}
return View();
}
[HttpPost]
[Authorize(Roles = "ExamAdmin")]
[ValidateAntiForgeryToken]
public ActionResult InformationSheet(string id, InformationSheetViewModel model)
{
using (var ctx = new AppXamApplicationEntities())
{
InformationConsentAndSheet query = ctx.InformationConsentAndSheets.Where(x => x.ExamID.Equals(id)).FirstOrDefault();
if (query != null)
{
//To insert picture into database as well as folder
string fileName = Path.GetFileNameWithoutExtension(model.ImageFile.FileName);
string extension = Path.GetExtension(model.ImageFile.FileName);
fileName = fileName + DateTime.Now.ToString("yymmssfff") + extension;
model.ImageURL = "~/Image/" + fileName;
fileName = Path.Combine(Server.MapPath("~/Image/"), fileName);
model.ImageFile.SaveAs(fileName);
query = new InformationConsentAndSheet()
{
ExamID = id,
ImageURL = model.ImageURL,
InformationSheetText = model.InformationSheetText,
InformationConsentForm = model.InformationConsentForm
};
ctx.SaveChanges();
}
else
{
//To insert picture into database as well as folder
string fileName = Path.GetFileNameWithoutExtension(model.ImageFile.FileName);
string extension = Path.GetExtension(model.ImageFile.FileName);
fileName = fileName + DateTime.Now.ToString("yymmssfff") + extension;
model.ImageURL = "~/Image/" + fileName;
fileName = Path.Combine(Server.MapPath("~/Image/"), fileName);
model.ImageFile.SaveAs(fileName);
query = new InformationConsentAndSheet()
{
ExamID = id,
ImageURL = model.ImageURL,
InformationConsentForm = model.InformationConsentForm,
InformationSheetText = model.InformationSheetText
};
ctx.InformationConsentAndSheets.Add(query);
ctx.SaveChanges();
}
return RedirectToAction("EditExam2");
}
}
I am very perplexed to what is wrong with my code, any form of help will be very appreciated as I am extremely new to MVC in general.

First of all you need to send your Id of survey when you want to edit. It can be done easly by route parameters in your form.
#using (Html.BeginForm("InformationSheet", "ExamAdmin", new { id = ViewBag.CurrentExamID }, FormMethod.Post, new { enctype = "multipart/form-data" }))
Your code for edit existing item is a little bit wrong. You need to, well, modify existing InformationConsentAndSheet and not creating new.
InformationConsentAndSheet query = ctx.InformationConsentAndSheets.Where(x => x.ExamID.Equals(id)).FirstOrDefault();
if (query != null)
{
// work with files
query.ExamID = id;
query.ImageURL = model.ImageURL;
query.InformationSheetText = model.InformationSheetText;
query.InformationConsentForm = model.InformationConsentForm;
ctx.Entry(query).State = EntityState.Modified;
ctx.SaveChanges();
}
Before submiting changes you need to specify that query have been edited and needs to be saved.
Hope its helps.

Related

Not able to retain original values after edit in mvc

i m not able to solve one issue that is , there are two files one is uploaded at the form filled up for the first time and the other file will be uploaded when the form willl be edited , but the issue is that, when the first uploaded file is shown in edit and no changes be made, that files gets blank , i used exclude as well but that does not made any effect.
My Controller methods:
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
FileDetails fileDetails = db.FileUpload.Find(id);
if (fileDetails == null)
{
return HttpNotFound();
}
return View(fileDetails);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Exclude= "FileBeforeTour,FileBeforeTourName")] FileDetails fileDetails)
{
if (ModelState.IsValid)
{
string uploadedfilename = Path.GetFileName(fileDetails.fileaftertourupload.FileName);
if (!string.IsNullOrEmpty(uploadedfilename))
{
string filenamewithoutextension = Path.GetFileNameWithoutExtension(fileDetails.fileaftertourupload.FileName);
string extension = Path.GetExtension(fileDetails.fileaftertourupload.FileName);
string filename = filenamewithoutextension + DateTime.Now.ToString("yymmssfff") + extension;
fileDetails.FileAfterTourName = filename;
fileDetails.FileAfterTour = "~/Content/Files/" + filename;
filename = Path.Combine(Server.MapPath("~/Content/Files"), filename);
fileDetails.fileaftertourupload.SaveAs(filename);
db.Entry(fileDetails).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
}
return View(fileDetails);
}
My Edit View:
#model OnlineStationaryRegister.Models.FileDetails
#{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
#using (Html.BeginForm("Edit", "File", FormMethod.Post, new { #enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>FileDetails</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.FileId)
<div class="form-group">
#Html.LabelFor(model => model.Officername, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Officername, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Officername, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Designation, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Designation, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Designation, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.FileBeforeTour, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
View File
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.FileAfterTour, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
<input type="file" name="fileaftertourupload" />
</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>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
My FileDetails model:
namespace OnlineStationaryRegister.Models
{
public class FileDetails
{
[Key]
public int FileId { get; set; }
public string Officername { get; set; }
public string Designation { get; set; }
public string FileBeforeTour { get; set; }
public string FileAfterTour { get; set; }
public string FileBeforeTourName { get; set; }
public string FileAfterTourName { get; set; }
public int status { get; set; } = 1;
[NotMapped]
public HttpPostedFileBase filebeforetourupload { get; set; }
[NotMapped]
public HttpPostedFileBase fileaftertourupload { get; set; }
}
}
It can be solved in many ways. One of the simplest ways as follows:
db.Entry(fileDetails).State = EntityState.Modified;
db.Entry(fileDetails).Property(x => x.FileBeforeTourName).IsModified = false; //<-- Here it is
db.Entry(fileDetails).Property(x => x.FileBeforeTour).IsModified = false; //<-- Here it is
db.SaveChanges();

ASP.NET MVC - cascading drop down not being saved to database

I created a 2-dependent cascading dropdown list:
Model Classes
COUNTRIES
public int COUNTRY_ID { get; set; }
public string COUNTRY_NAME { get; set; }
STATES
public int STATE_ID { get; set; }
public Nullable<int> COUNTRY_ID { internal get; set; }
public string STATE_NAME { get; set; }
CITIES
public int CITY_ID { get; set; }
public Nullable<int> STATE_ID { internal get; set; }
public Nullable<int> COUNTRY_ID { internal get; set; }
public string CITY_NAME { get; set; }
The drop-down list worked as expected. But when clicked on save button, nothing is saved.
Controller: CITIES
// Json Call to get state
public JsonResult GetStates(string id)
{
List<SelectListItem> states = new List<SelectListItem>();
var stateList = this.Getstate(Convert.ToInt32(id));
var stateData = stateList.Select(m => new SelectListItem()
{
Text = m.STATE_NAME,
Value = m.STATE_ID.ToString(),
});
return Json(stateData, JsonRequestBehavior.AllowGet);
}
// Get State from DB by country ID
public IList<STATES> Getstate(int CountryId)
{
return _statesService.GetStates().Where(stat => stat.COUNTRY_ID == CountryId).ToList();
}
//
public ActionResult Create()
{
ViewBag.COUNTRIES = new SelectList(_countriesService.GetCountries(), "COUNTRY_ID", "COUNTRY_NAME");
ViewBag.STATES = new SelectList(_statesService.GetStates(), "STATE_ID", "state_NAME");
return View();
}
[HttpPost]
public ActionResult Create(CITIES cities)
{
try
{
IEnumerable<COUNTRIES> lstCountries = _countriesService.GetCountries();
if (ModelState.IsValid)
{
cities.ACTION_STATUS = 0;
cities.CREATED_DATE = DateTime.Now;
_citiesService.AddCity(cities);
return RedirectToAction("Index");
}
}
catch
{
ModelState.AddModelError("", "We cannot add this Cities. Verify your data entries !");
}
ViewBag.COUNTRIES = new SelectList(_countriesService.GetCountries(), "COUNTRY_ID", "COUNTRY_NAME", cities.COUNTRY_ID);
ViewBag.STATES = new SelectList(_statesService.GetStates(), "STATE_ID", "STATE_NAME", cities.STATE_ID);
return View(cities);
}
View : Cities
<div class=" box box-body box-primary">
#*#using (Html.BeginForm())*#
#using (Html.BeginForm("Create", "Cities", FormMethod.Post, new { #class = "form-horizontal", #enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.ValidationSummary(true, null, new { #class = "text-danger" })
<div class="row .col">
<div style="margin-top:20px" class="mainbox col-md-12 col-md-offset-0 col-sm-8 col-sm-offset-2">
<div class="panel panel-info">
<div class="panel-heading">
<div class="panel-title">Create City</div>
</div>
<div class="panel-body">
<div class="col-md-4">
<div>
#Html.LabelFor(model => model.COUNTRY_ID, "Country Name", new { #class = "control-label" })
#Html.DropDownList("COUNTRIES", ViewBag.COUNTRIES as SelectList, "-- Please Select a Country --", new { style = "width:250px" })
#Html.ValidationMessageFor(model => model.COUNTRY_ID, null, new { #class = "text-danger" })
</div>
</div>
<div class="col-md-4">
<div>
#Html.LabelFor(model => model.STATE_ID, "State Name", new { #class = "control-label" })
#Html.DropDownList("STATES", new SelectList(string.Empty, "Value", "Text"), "-- Please select a State --", new { style = "width:250px", #class = "dropdown1" })
#Html.ValidationMessageFor(model => model.STATE_ID, null, new { #class = "text-danger" })
</div>
</div>
<div class="col-md-4">
<div>
#Html.LabelFor(model => model.CITY_NAME, "City Name", new { #class = "control-label" })
#Html.TextBoxFor(model => model.CITY_NAME, new { #style = "border-radius:3px;", #type = "text", #class = "form-control", #placeholder = Html.DisplayNameFor(m => m.CITY_NAME), #autocomplete = "on" })
#Html.ValidationMessageFor(model => model.CITY_NAME, null, new { #class = "text-danger" })
</div>
</div>
</div>
<div class="panel-footer">
<div class="panel-title">
<div class="form-actions no-color">
<input type="submit" value="Create" class="btn btn-success" />
</div>
</div>
</div>
</div>
</div>
</div>
</div>
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
<script type="text/javascript">
$(document).ready(function () {
//Country Dropdown Selectedchange event
$("#COUNTRIES").change(function () {
$("#STATES").empty();
$.ajax({
type: 'POST',
url: '#Url.Action("GetStates")', // Calling json method
dataType: 'json',
data: { id: $("#COUNTRIES").val() },
// Get Selected Country ID.
success: function (states) {
$.each(states, function (i, state) {
$("#STATES").append('<option value="' + state.Value + '">' +
state.Text + '</option>');
});
},
error: function (ex) {
alert('Failed to retrieve states.' + ex);
}
});
return false;
})
});
</script>
When I clicked on Countries Drop-down list, it populates and the respective states are loaded as expected. When I clicked on Save button, nothing is being saved. Please what is the problem

insert many images which are in icollection in other model in asp mvc

My first model
public class article
{
public int id { set; get; }
public string title { set; get; }
public string bodyofarticle { set; get; }
public ICollection <image> images { set; get; }
}
The second model which I use to store the images URL in the database
public class image
{
public int id { set; get; }
public int articleid { set; get; }
public string url{ set; get; }
}
Form to enter the values its okay to enter the
article model but when I put any image it did not insert into the database
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>article</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.title, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.title, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.title, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.bodyofarticle, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextAreaFor(model => model.bodyofarticle, new { htmlAttributes = new { #class = "form-control" }, cols = 60, rows = 10 } )
#Html.ValidationMessageFor(model => model.bodyofarticle, "", new { #class = "text-danger" })
</div>
<input type="file" multiple id="file" name="file" />
</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 look at the break point I see that files are empty and I have tried many ways but it's still empty
public ActionResult Create([Bind(Include = "id,title,bodyofarticle")] article article , HttpPostedFileBase files)
{
if (ModelState.IsValid)
{
if (files != null && files.ContentLength >0 )
{
foreach (string file in Request.Files)
{
var v = Request.Files[file];
var fileName = Path.GetFileName(files.FileName);
var path = Path.Combine(Server.MapPath("~/images/"), fileName);
files.SaveAs(path);
image imag = new image();
imag.url = Url.Content("~/images/" + fileName);
article.images.Add(imag);
}
}
db.articles.Add(article);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(article);
}
Try this in the view. You will need to post the file from view.
#using (Html.BeginForm("Create", null, FormMethod.Post, new { enctype = "multipart/form-data" }))
Hope you used the command add-migrations before using the update-database command.
//it was at the view
#using (Html.BeginForm("Create", null, FormMethod.Post, new { enctype = "multipart/form-data" }))
//thnx vikrim and jim

MVC dropdown update the form not working

I know there are other posts for this, but I'm still not wrapping my head around it.
I would like write my MVC view to use a ViewModel that will allow text fields to change based on selections in a dropdownlist?
In the below example, I have a collection of Templates. I would like to see the dropdown list the Templates, then the user would select an item from the DropDownList, the code would populate the text fields, the user could edit those text fields, and finally submit the form with their changes.
What happens now is that every time the Submit code is called, it acts as if the dropdown was selected.
Model and ViewModel:
public class Template
{
[Display(Name = "Template")]
public int TemplateId {get; set;}
public string TemplateName {get; set;}
public string Subject {get; set;}
public string Message {get; set;}
}
public class TemplateViewModel
{
[Display(Name = "Template")]
public int LetterTemplateId {get; set;}
public ICollection<Template> Templates { get; set; }
}
View:
#model MyWebProject.ViewModels.TemplateViewModel
#using (Html.BeginForm("callforcontentproposaldetail","Project", FormMethod.Post, new {id = "frmProposalDetail"}))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Email Template</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.LetterTemplateId, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-4">
#Html.DropDownListFor(m => m.LetterTemplateId,
new SelectList(Model.Templates as IEnumerable, "TemplateId", "TemplateName"),
new { #class = "form-control", onchange = "document.getElementById('frmProposalDetail').submit();", id = "ddlTemplate" })
#Html.ValidationMessageFor(model => model.LetterTemplateId, "", new { #class = "text-danger" })
</div>
</div>
<div id="letterTemplateEditArea">
<div class="form-group col-md-12">
<div class="row">
#Html.TextBoxFor(model => model.SelectedTemplate.TemplateSubject, new { #class = "form-control" })
#Html.LabelFor(model => model.SelectedTemplate.Message, new { #class = "control-label" })
#Html.TextAreaFor(model => model.SelectedTemplate.Message, new { #class = "form-control", #style = "max-width:100%;height:400px;font-size:11px;" })
</div>
</div>
<br />
</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>
}
Controller Code:
public ActionResult callforcontentproposaldetail(Guid id)
{
var proposal = db.CallForContentProposal.Find(id);
var model = Mapper.Map<CallForContentProposalViewModel>(proposal);
if (TempData["LetterTemplateId"] != null)
{
var emailTempId = 0;
if (int.TryParse((string)TempData["LetterTemplateId"], out emailTempId))
{
var template = model.Templates.FirstOrDefault(t => t.TemplateId == emailTempId);
model.SelectedTemplateId = emailTempId;
model.Subject = template.Subject;
model.Message = template.Body;
}
}
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult callforcontentproposaldetail(CallForContentProposalViewModel model, string SelectedTemplateId = "")
{
// Do some saving of the current data
// ...
db.SaveChanges();
// If this is the dropdownlist calling, redirect back to display the text fields populated
if (!string.IsNullOrEmpty(SelectedTemplateId))
{
TempData["LetterTemplateId"] = SelectedTemplateId;
return RedirectToAction("callforcontentproposaldetail", new { id = model.CallForContentProposalId });
}
// On Submit, do other tasks
return RedirectToAction("callforcontent", new {id = model.CallForContentId});
}
I figured it out. Thanks to your comments, I worked out a way to use Ajax to do this for our situation (I tried to make it as generic as possible to reuse elsewhere):
View:
<div class="form-group">
#Html.LabelFor(model => model.LetterTemplateId, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-4">
#Html.DropDownListFor(m => m.LetterTemplateId,
Model.EmailTemplateSelect(),
new { #class = "form-control", onchange = "EmailTemplateLoad.call(this,this.options[this.selectedIndex].value,'LetterTemplateSubject','LetterTemplateBody');", id = "ddlReviewRole" })
#Html.ValidationMessageFor(model => model.LetterTemplateId, "", new { #class = "text-danger" })
</div>
</div>
Javascript:
EmailTemplateLoad = function(id, subjectTbName, messageTbName) {
debugger;
var ServiceUrl = "/Project/LoadTemplate?id=" + id;
var content = '';
$.support.cors = true;
$.ajax({
type: 'GET',
url: ServiceUrl,
async: true,
cache: false,
crossDomain: true,
contentType: "application/json; charset=utf-8",
dataType: 'json',
error: function (xhr, err) {
},
success: function (result, status) {
$('#' + subjectTbName).val(result[0].Subject);
$('#' + messageTbName).val(result[0].Message);
}
});
};
Controller:
public ActionResult LoadTemplate(int id)
{
var result = (from t in db.EmailBodyTemplate
where t.EmailTemplateId == id
select new
{
t.Subject,
Message = t.Body
});
return Json(result, JsonRequestBehavior.AllowGet);
}
I also followed this example, although it was incomplete:
CodeProject example

How can you pass a list of objects in a model back to a controller? [duplicate]

This question already has answers here:
Model Binding to a List MVC 4
(3 answers)
Closed 9 years ago.
UPDATE: The solution was to use an EditorTemplate. See solution below:
I want to pass a model to/from a controller which let's me set name, and set the value on an undetermined roles (as checkboxes). When I examine the postback, I get a value for Name in model, but Roles is null. How can I tell which checkboxes were checked?
Model:
public class MyModel
{
public string Name { get; set; }
public IEnumerable<RoleItem> Roles { get; set; }
}
public class RoleItem
{
public String Name { get; set; }
public String Id { get; set; }
public bool Selected { get; set; }
public RoleItem(String id, String name, bool selected = false)
{
this.Name = name;
this.Id = id;
this.Selected = selected;
}
}
Razor:
#model WebApplication1.Controllers.MyModel
#using (Html.BeginForm("Index", "Home", FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary()
#Html.TextBoxFor(m=>m.Name)
foreach (var m in Model.Roles)
{
<div>
#Html.Label(m.Id, m.Name)
#Html.CheckBox(m.Id, m.Selected, new { id = #m.Id })
</div>
}
<input type="submit"/>
}
GOAL: To allow any Administrator to add new users to the Asp identity tables and assign them roles that are defined in a list using checkboxes.
Model:
public class RegisterViewModel
{
[Display(Name = "Name")]
public string FullName { get; set; }
[Required]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
public List<RoleItem> Roles { get; set; }
}
public class RoleItem
{
public String Name { get; set; }
public String Id { get; set; }
public bool IsMember { get; set; }
}
Controller (GET): This reads all of the roles in the database and transforms them to a list of RoleItems. This will prepend the character "r" onto the id field as some browsers have a problem with an id starting with a number. We want to make sure the "Users" group is checked by default, so we find this value in the list and set the IsMember property to true. We check the request to see if the page was redirected here from a successful POST (see below)
// GET: /Account/AddUser
[Authorize(Roles = "Administrators")]
public ActionResult AddUser()
{
var rolesDb = new ApplicationDbContext(); //Users, Administrators, Developers, etc
ViewBag.AddSuccess = Request["added"]=="1" ? true : false;
var roleItems = rolesDb.Roles.Select(r => new RoleItem() { Id = "r" + r.Id, Name = r.Name, IsMember = false }).ToList(); //add an r to get around a browser bug
var users = roleItems.FirstOrDefault(r => r.Name == "Users"); //Get the row that has the Users value and set IsMember=true
if (users != null)
users.IsMember = true;
var m = new RegisterViewModel() {Roles = roleItems};
return View(m);
}
View: Pretty standard stuff. Note #Html.EditorFor(x => x.Roles) at the bottom, which uses an editor template (follows)
#model cherry.Models.RegisterViewModel
#{
ViewBag.Title = "AddUser";
}
<h2>#ViewBag.Title.</h2>
#if (Convert.ToBoolean(ViewBag.AddSuccess))
{
<text>User added!</text>
}
#using (Html.BeginForm("AddUser", "Account", FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
#Html.AntiForgeryToken()
<h4>Create a new account.</h4>
<hr />
#Html.ValidationSummary()
<div class="form-group">
#Html.LabelFor(m => m.EmailAddress, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.EmailAddress, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.FullName, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.FullName, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.Password, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.PasswordFor(m => m.Password, new { value = Model.Password, #class = "form-control" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.ConfirmPassword, new {#class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.PasswordFor(m => m.ConfirmPassword, new {value = Model.ConfirmPassword, #class = "form-control" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-default" value="Register" />
</div>
</div>
#Html.EditorFor(x => x.Roles)
}
EditorTemplate:
You MUST give the template the same name as the object you are creating the template for. You must also put this object in a folder called EditorTemplates below the view you are designing this for, or you can put the folder inside the shared folder.
Views\Account\EditorTemplates\RoleItem.cshtml
#model cherry.Models.RoleItem
<div>
#Html.CheckBoxFor(x => x.IsMember)
#Html.LabelFor(x => x.IsMember, Model.Name)
#Html.HiddenFor(x => x.Name)
</div>

Resources