MVC ModelState.Clear isn't updating ModelState - asp.net-mvc

In my MVC program, once the user has submitted an edit form, those values for the object get saved in the model server-side, but the previous values show up in the view.
I know it has to do with MVC's validation process, where it first checks ModelState before the server side values. The solution I've read across the forums is to clear the ModelState. The only problem is, ModelState.Clear isn't working for me.
Help please.
Model
public class Help
{
[HiddenInput(DisplayValue=true)]
public int HelpID { get; set; }
[Required(ErrorMessage = "Please enter a proper URL")]
public string URL { get; set; }
[Required(ErrorMessage = "Please enter a content description:")]
[DataType(DataType.MultilineText)]
public string HelpContent { get; set; }
/*? 2 properites are nullable*/
public DateTime? createDateTime { get; set; }
public DateTime? modifiedDateTime { get; set; }
}
Controller
/*Create the admin controller*/
public class AdminController : Controller
{
//declare interface object
private IHelpRepository repository;
/*Pass a db interface to controller*/
public AdminController(IHelpRepository repo)
{
repository = repo;
}
/*default admin screen. displays help table obs*/
public ViewResult Index()
{
return View();
}
/*Returns add view form*/
public ActionResult AddForm()
{
return PartialView();
}
/*Will handle the post for the add screen after user has
submitted add information*/
[HttpPost]
[ValidateInput(false)] //this allows admin to place html in field
public ActionResult AddForm(Help help)
{
if (ModelState.IsValid) //if all fields are validated
{
//set the edit date
help.createDateTime = DateTime.Now;
repository.SaveHelp(help);
return (null); //return "null" to div so control is given back to main view
}
else //there is something wrong. send back to view
{
return PartialView(help);
}
}
/*Returns edit view form, searches for object to edit with id
if no id provided, 0 by default*/
public ActionResult EditForm(int helpID = 0)
{
Help help = repository.Help.FirstOrDefault(q => q.HelpID == helpID);
help.HelpContent = System.Web.HttpUtility.HtmlDecode(help.HelpContent);
return PartialView(help);
}
/*Will handle the post for the edit screen after user has
submitted edit information*/
[HttpPost]
[ValidateInput(false)] //this allows admin to place html in field
public ActionResult EditForm(Help help)
{
if (ModelState.IsValid) //if all fields are validated
{
//set the edit date
help.modifiedDateTime = DateTime.Now;
repository.SaveHelp(help);
ModelState.Clear();
return (null); //return "null" to div so control is given back to main view
}
else //there is something wrong. send back to view
{
return PartialView(help);
}
}
/*Delete action method, searches with id*/
[HttpPost]
public ActionResult Delete(int helpId)
{
Help helpDel = repository.Help.FirstOrDefault(p => p.HelpID == helpId);
if (helpDel != null) //if the object is found, delete
{
repository.DeleteHelp(helpDel);
}
//in all cases return to index
return RedirectToAction("Index");
}
/*Used by the telerik table to rebind grid*/
[GridAction]
public ActionResult AjaxBinding()
{
return View(new GridModel(repository.Help));
}
}//end admin controller class`
Partial View (Gets loaded into a div)
`
#using (Html.BeginForm(null, null, FormMethod.Post, new { id = "Editx" }))
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Edit Entry</legend>
#Html.HiddenFor(model => model.HelpID)
<div class="editor-label">
#Html.LabelFor(model => model.URL)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.URL)
#Html.ValidationMessageFor(model => model.URL)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.HelpContent, "Help Content")
</div>
<div class="editor-field">
#{
Html.Telerik().EditorFor(content => content.HelpContent)
.Name("HelpContent")
.FileBrowser(settings => settings
.Browse("Browse", "ImageBrowser")
.Thumbnail("Thumbnail", "ImageBrowser")
.Upload("Upload", "ImageBrowser")
.DeleteFile("DeleteFile", "ImageBrowser")
.DeleteDirectory("DeleteDirectory", "ImageBrowser")
.CreateDirectory("CreateDirectory", "ImageBrowser")
)
.Render();
}
#Html.ValidationMessageFor(model => model.HelpContent)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.createDateTime, "Create Date")
</div>
<div class="editor-field">
#Html.EditorFor(model => model.createDateTime)
#Html.ValidationMessageFor(model => model.createDateTime)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.modifiedDateTime, "Modified Date")
</div>
<div class="editor-field">
#Html.EditorFor(model => model.modifiedDateTime)
#Html.ValidationMessageFor(model => model.modifiedDateTime)
</div>
<p>
<input id="btnEdit" type="submit" value="Save" />
<button id="btnCancel">Cancel</button>
</p>
</fieldset>
}

After trolling through about a hundred links, I found the solution to my problem. ModelState.Clear actually clears the objects values in the controller, but for whatever reason was still displaying the old values in the view. Maybe its because I load/unload my edit form into a div tag? Don't ask. I don't know. The solution that works for me is this:
$.ajax({
url: "somecontroller/someAction,
cache: false, // this is key to make sure JQUERY does not cache your request
success: function( data ) {
alert( data ); } });
I had to set the "cache" setting to "false".
Thanks to #minus4 for the solution, bruh.

Related

MVC model validation

So, im currently building an application that needs the user model validating, and if the incorrect properties are filled in to the user it will tell them.
I have the data annotations set up, but im not sure how i relay the error message back to the user?
I have this set up so far on my model and view.
Model
public class DatabaseModel
{
[Required(ErrorMessage = ("A first name is required"))]
public string FirstName { get; set; }
[Required(ErrorMessage = ("A last name is required"))]
public string LastName { get; set; }
[Required(ErrorMessage = ("A valid role is required"))]
public string Role { get; set; }
// TODO - Validate rank to only b 1 - 10
//
[Range(1,10, ErrorMessage = ("A rank between 1 and 10 is required"))]
public int Rank { get; set; }
}
And View
#model RoleCreatorAndEditor.Models.DatabaseModel
#{
ViewData["Title"] = "Index";
}
<h2>User Information</h2>
<p>This is your user information!</p>
#using (Html.BeginForm("Index", "Home", FormMethod.Post)) {
#Html.Label("First Name")
<br>
#Html.TextBoxFor(m => m.FirstName)
<br>
#Html.Label("Last Name")
<br>
#Html.TextBoxFor(m=>m.LastName)
<br>
#Html.Label("Role")
<br>
#Html.TextBoxFor(m => m.Role)
<br>
#Html.Label("Rank")
<br>
#Html.TextBoxFor(m => m.Rank)
<br><br>
<input type="submit" value="Save">
}
My Controller
public class HomeController : Controller
{
// GET: Home
[HttpGet]
public ActionResult Index()
{
DatabaseModel model = new DatabaseModel();
return View(model);
}
[HttpPost]
public ActionResult Index(DatabaseModel model)
{
if (ModelState.IsValid)
{
ListToDatatable convert = new ListToDatatable();
DataTable user = convert.Convert(model);
DatabaseRepository dbRepo = new DatabaseRepository();
dbRepo.Upload(user);
}
return View();
}
}
I believe the model needs to be passed back to the view in order to display the error message, and although i have read through the documentation on asp.net i cannot understand how they just add the error message and the form knows how to display the errors to the user.
I am extremely confused.
You need to use ModelState.IsValid in your Controller and also #Html.ValidationMessageFor(model => model.FirstName) in your view:
public ActionResult Index(ViewModel _Model)
{
// Checking whether the Form posted is valid one.
if(ModelState.IsValid)
{
// your model is valid here.
// perform any actions you need to, like database actions,
// and/or redirecting to other controllers and actions.
}
else
{
// redirect to same action
return View(_Model);
}
}
For your example:
#model RoleCreatorAndEditor.Models.DatabaseModel
#{
ViewData["Title"] = "Index";
}
<h2>User Information</h2>
<p>This is your user information!</p>
#using (Html.BeginForm("Index", "Home", FormMethod.Post)) {
#Html.LabelFor(m=>m.FirstName)
<br>
#Html.TextBoxFor(m => m.FirstName)
#Html.ValidationMessageFor(model => model.FirstName, "", new { #class = "text-danger" })
<br>
#Html.LabelFor(m=>m.LastName)
<br>
#Html.TextBoxFor(m=>m.LastName)
#Html.ValidationMessageFor(model => model.LastName, "", new { #class = "text-danger" })
. . .
<input type="submit" value="Save">
}
Controller:
[HttpPost]
public ActionResult Index(DatabaseModel model)
{
if (ModelState.IsValid)
{
ListToDatatable convert = new ListToDatatable();
DataTable user = convert.Convert(model);
DatabaseRepository dbRepo = new DatabaseRepository();
dbRepo.Upload(user);
}
return View(model);
}

ICollection from ViewModel showing up as null, modelstate is not valid in MVC4

I have the following viewModel:
public class CreateCardViewModel
{
[HiddenInput(DisplayValue = false)]
public int SetId { get; set; }
[Required]
public ICollection<Side> Sides { get; set; }
[Required]
[DataType(DataType.Date)]
public DateTime DateCreated { get; set; }
[Required]
public bool IsReady { get; set; }
}
And the following actions defined for Create:
[HttpGet]
public ActionResult Create(int setId)
{
var model = new CreateCardViewModel();
// attach card to current set
model.SetId = setId;
// create a new Side
var side = new Side() {Content = "Blank Side"};
// Add this to the model's Collection
model.Sides = new Collection<Side> { side };
return View(model);
}
[HttpPost]
public ActionResult Create(CreateCardViewModel viewModel)
{
if (ModelState.IsValid)
{
var set = _db.Sets.Single(s => s.SetId == viewModel.SetId);
var card = new Card {Sides = viewModel.Sides};
set.Cards.Add(card);
_db.Save();
}
return View(viewModel);
}
When I try to create a new card, the Sides property of the viewModel is null, so the ModelState is coming up as null. I can't quite figure out why that initial Side isn't getting passed with the model.
My View looks like this:
<h2>Create</h2>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>CreateCardViewModel</legend>
<div class="editor-label">
#Html.LabelFor(model => model.SetId)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.SetId)
#Html.ValidationMessageFor(model => model.SetId)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.DateCreated)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.DateCreated)
#Html.ValidationMessageFor(model => model.DateCreated)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.IsReady)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.IsReady)
#Html.ValidationMessageFor(model => model.IsReady)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
There's nothing in your View that is bound to the Sides property of your ViewModel. Without anything in the form to hold the value of that property, it will be null when model binding occurs. You'll need to somehow capture the Side in your form - how are you adding to/removing from this property? Via user interaction that should take place on the form?
Try adding a second argument on Create() of type ICollection<Side> and see if that gets anything passed to it.

Error when send request from partial view

I'm trying to learn the basics of MVC (NerdDinner tutorial). I have defined a model:
public class DinnerFormViewModel
{
// Properties
public Dinner Dinner { get; private set; }
public SelectList Countries { get; private set; }
// Constructor
public DinnerFormViewModel(Dinner dinner)
{
Dinner = dinner;
Countries = new SelectList(PhoneValidator.Countries, dinner.Country);
}
}
and I defined a partial view:
#model MyNerddiner.Models.DinnerFormViewModel
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Dinner</legend>
#Html.HiddenFor(model => model.Dinner.DinnerID)
<div class="editor-label">
#Html.LabelFor(model => model.Dinner.Title)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Dinner.Title)
#Html.ValidationMessageFor(model => model.Dinner.Title)
</div>
</fieldset>
}
which is loaded from usual view:
#model MyNerddiner.Models.DinnerFormViewModel
#{
ViewBag.Title = "Create";
}
<div id="Create" >
<h2>Host a Dinner</h2>
#Html.Partial("_DinnerForm")
</div>
The controller:
public ActionResult Create()
{
Dinner dinner = new Dinner()
{
EventDate = DateTime.Now.AddDays(7)
};
return View(new DinnerFormViewModel(dinner));
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(DinnerFormViewModel dinnerViewModel)
{
Dinner dinner = null;
if (ModelState.IsValid)
{
try
{
dinner = dinnerViewModel.Dinner;
UpdateModel(dinner);
dinnerRepository.Add(dinner);
dinnerRepository.Save();
return RedirectToAction("Details", new { id = dinner.DinnerID });
}
catch
{
ModelState.AddRuleViolations(dinner.GetRuleViolations());
return View(dinner);
}
}
return View(new DinnerFormViewModel(dinner));
}
Now when I'm trying to create (on postback), I'm getting an error:
No parameterless constructor defined for this object.
I can guess that it is because somewhere the program is trying to initiate the DinnerFormViewModel, but where, and why and how should I make it right?
The MVC framework needs your view model to have a constructor that takes no parameters so that it can create an empty instance to populate with data from the request. DinnerFormViewModel does not implement a constructor with no parameters, add one, and this will fix your issue.
Well, found the problem and it have nothing to do with model and constructor.
the problem was that view contained following row:
#Html.DropDownListFor(model => model.Countries, Model.Countries)
#Html.ValidationMessageFor(model => model.Countries)
When i checked from where the exception came- it come because the country value was null.
After i changed
model => model.Countries
to
model => model.Dinner.Country
the exception stoped to be thrown
I'm so glad i solve this on my own!

How to save data into DataTable in MVC3?

I have mvc3 application in this i have used two partial views 1.controls 2.webgrid
inside controls i'm populating dropdownlists from actual database tables. using EF
On index.cshtml i have one form in which need to select values from these dropdown lists and when press insert button these values should have to go to Temp "DataTable" and also show it in webgrid...I'm newbie to MVC3 and dont know how to do this.
Controls.cshtml
#model Mapping.Models.SecurityIdentifierMappingViewModel
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Mapping</legend>
<div class="editor-label">
#Html.Label("Pricing SecurityID")
</div>
<div class="editor-field">
#Html.HiddenFor(model => model.MappingControls.Id)
#Html.DropDownListFor(model => model.MappingControls.PricingSecurityID,
new SelectList(Model.PricingSecurities, "Value", "Text"),
"Select SecurityID"
)
#Html.ValidationMessageFor(model => model.MappingControls.PricingSecurityID)
</div>
<div class="editor-label">
#Html.Label("CUSIP ID")
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.MappingControls.CUSIP,
new SelectList(Model.CUSIPs, "Value", "Text"),
"Select CUSIP"
)
#Html.ValidationMessageFor(model => model.MappingControls.CUSIP)
</div>
<div class="editor-label">
#Html.Label("Calculation")
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.MappingControls.Calculation)
#Html.ValidationMessageFor(model => model.MappingControls.Calculation)
</div>
<p>
<input id="btnsubmit" type="submit" value="Insert" />
</p>
</fieldset>
}
HomeController.cs
public class HomeController : Controller
{
//
// GET: /Home/
mydataEntities dbContext = new mydataEntities();
DataRepository objRepository = new DataRepository();
//GET
public ActionResult Index(string userAction , int uid = 0)
{
var mappingobj = new SecurityIdentifierMappingViewModel();
mappingobj.MappingWebGridList = dbContext.SecurityIdentifierMappings.ToList();
mappingobj.MappingControls = new SecurityIdentifierMapping();
mappingobj.MappingControls.PricingSecurityID = 0;
mappingobj.MappingControls.CUSIP = string.Empty;
mappingobj.PricingSecurities = objRepository.GetPricingSecurityID();
mappingobj.CUSIPs = objRepository.GetCUSIP();
return View(mappingobj);
}
//POST
[HttpPost]
public ActionResult Index(SecurityIdentifierMappingViewModel objModel)
{
if (objModel.MappingControls.Id > 0)
{
if (ModelState.IsValid)
{
dbContext.Entry(objModel.MappingControls).State = EntityState.Modified;
try
{
dbContext.SaveChanges();
//objModel = new SecurityIdentifierMappingViewModel();
//return RedirectToAction("Index", "Home");
}
catch (System.Data.Entity.Validation.DbEntityValidationException ex)
{
throw;
}
}
}
//insert code
else
{
if (ModelState.IsValid)
{
dbContext.SecurityIdentifierMappings.Add(objModel.MappingControls);
try
{
dbContext.SaveChanges();
}
catch (System.Data.Entity.Validation.DbEntityValidationException ex)
{
throw;
}
}
}
return RedirectToAction("Index");
}
}
public class SecurityIdentifierMappingViewModel
{
public IEnumerable<SecurityIdentifierMapping> MappingWebGridList { get; set; }
public SecurityIdentifierMapping MappingControls { get; set; }
public List<SelectListItem> PricingSecurities { get; set; }
public List<SelectListItem> CUSIPs { get; set; }
}
Currently using SecurityIdentifierMapping as a 3rd table from database in which inserting my form data ... but need to insert it into "DataTable"
You will have to create a DataTable object and assign appropriate DataColumn objects to it. After that map your SecurityIdentifierMapping properties to columns in your temporary data table. As for mapping DataTable to WebGrid, I am not going to say that it is not possible as I have never tried this thing personally, but you will have to map it back to a collection of SecurityIdentifierMapping.
But, why do you need DataTable? What possible advantages could DataTable have over IQueryable or IEnumerable? What is it that you actually want to achieve using this strategy?
UPDATE:
You are already using IEnumerable in your ViewModel class (SecurityIndentifierMappingViewModel). At the same time you are storing data in the database when POSTing to Index, and fetching again in GET version of Index.
What you are missing is to create a WebGrid object in your view. Your view could be defined like this:
#{
var columns = new List<string>();
columns.Add("Column 1");
columns.Add("Column 2");
var grid = new WebGrid(model: Model.MappingWebGridList, columnNames: columns);
}
#grid.GetHtml()
Place the above code somewhere in your Index view, and define your own columns. In addition, have a look at this article which I wrote in order to get more ideas what you can do with WebGrid http://apparch.wordpress.com/2012/01/04/webgrid-in-mvc3/.
I hope I managed to help you at least a bit.

how to select dropdownlist value and display in mvc3?

I have an MVC3 web application. On index.cshtml I have two dropdown lists. When I select from those lists I need to click on a next button and I want to display the selected values. How can i do this?
homecontroller.cs
DataRepository objRepository = new DataRepository();
public ActionResult Index()
{
ViewModel objViewModel = new ViewModel();
objViewModel.ID = objRepository.GetPricingSecurityID();
objViewModel.ddlId = objRepository.GetCUSIP();
return View(objViewModel);
}
ViewModel.cs
public class ViewModel
{
//DDL ID
[Required(ErrorMessage = "Please select a PricingSecurityID")]
public List<SelectListItem> ddlId { get; set; }
//DropDownList Values
[Required(ErrorMessage = "Please select a PricingSecurityID")]
public List<SelectListItem> ID { get; set; }
}
index.cshtml
<div class="editor-label">
#Html.Label("Pricing SecurityID")
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.ID,
new SelectList(Model.ID, "Value", "Text"),
"-- Select category --"
)
#Html.ValidationMessageFor(model => model.ID)
</div>
<div class="editor-label">
#Html.Label("CUSIP ID")
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.ddlId,
new SelectList(Model.ddlId, "Value", "Text"),
"-- Select category --"
)
#Html.ValidationMessageFor(model => model.ddlId)
</div>
<p>
<input type="submit" value="Next" />
</p>
How can I display selected values?
If your requirement is to build some kind of wizard, you need a way of maintaining state between steps.
ViewBag is no good for this because you should be following the PRG (Post/Redirect/Get) pattern for each wizard step.
TempData would work for navigating forward between steps but will fall over if the user goes back or navigates to a step directly.
You therefore need something with a longer lifetime. The ASP.NET Session object or a database are both good candidates for this.
Here's an example:
public class WizardController : Controller
{
public ActionResult Step1()
{
var session = GetWizardSession();
if (session.Step1 == null)
{
session.Step1 = new Step1View
{
PricingSecurityIds = new SelectList(new[] { 1, 2, 3, 4, 5 }),
SomeOtherIds = new SelectList(new[] { 1, 2, 3, 4, 5 })
};
}
return View(session.Step1);
}
[HttpPost]
public ActionResult Step1(Step1View cmd)
{
var session = GetWizardSession();
// save the wizard state
session.Step1.SelectedPricingSecurityId = cmd.SelectedPricingSecurityId;
session.Step1.SelectedSomeOtherId = cmd.SelectedSomeOtherId;
// now onto step 2
session.Step2 = new Step2View
{
PricingSecurityId = cmd.SelectedPricingSecurityId,
SomeOtherId = cmd.SelectedSomeOtherId,
Name = "John Smith"
};
return RedirectToAction("step2");
}
public ActionResult Step2()
{
return View(GetWizardSession().Step2);
}
public WizardSession GetWizardSession()
{
var session = Session["wizardsession"];
if (session == null)
{
session = new WizardSession();
Session["wizardsession"] = session;
}
return session as WizardSession;
}
}
public class Step1View
{
public SelectList PricingSecurityIds { get; set; }
public SelectList SomeOtherIds { get; set; }
public int SelectedPricingSecurityId { get; set; }
public int SelectedSomeOtherId { get; set; }
}
public class Step2View
{
public int PricingSecurityId { get; set; }
public int SomeOtherId { get; set; }
public string Name { get; set; }
}
public class WizardSession
{
public Step1View Step1 { get; set; }
public Step2View Step2 { get; set; }
}
In Step1 we make a call to GetWizardSession. This returns an object from the ASP.NET Session that contains all of the information we have collected for each step in the wizard. In this example we simply store the ViewModel for each step (i.e. session.Step1).
We check to see if Step1 exists in the session and create it if it doesn't. We then pass the Step1 model to our view.
When the user submits the form we update the "Selected" values in session.Step1. This ensures that if the user navigates back to /step1, we "remember" their values. We then build the model for Step2 and save it in the session.
When we navigate to /step2 we assume that a model exists in the session (because they should have got here from step1) so we just return return View(GetWizardSession().Step2);
The views:
Step 1
#model MvcWizardDemo.Controllers.Step1View
#{
ViewBag.Title = "Step1";
}
<h2>Step1</h2>
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Step1View</legend>
<div class="editor-label">
#Html.LabelFor(m => m.PricingSecurityIds)
</div>
<div class="editor-field">
#Html.DropDownListFor(m => m.SelectedPricingSecurityId, Model.PricingSecurityIds)
#Html.ValidationMessageFor(m => m.PricingSecurityIds)
</div>
<div class="editor-label">
#Html.LabelFor(m => m.SomeOtherIds)
</div>
<div class="editor-field">
#Html.DropDownListFor(m => m.SelectedSomeOtherId, Model.SomeOtherIds)
#Html.ValidationMessageFor(m => m.SomeOtherIds)
</div>
<p>
<input type="submit" value="Next" />
</p>
</fieldset>
}
Step 2
#model MvcWizardDemo.Controllers.Step2View
#{
ViewBag.Title = "Step2";
}
<h2>Step2</h2>
Hi, #Model.Name you selected the following values in the previous step:
<p>
<strong>Security Id:</strong> #Model.PricingSecurityId
</p>
<p>
<strong>Some other Id:</strong> #Model.SomeOtherId
</p>
Try this it should work :
[HttpPost]
public ActionResult Index(ViewModel model)
{
// put what you want to show
}

Resources