ModelState.Clear is not working - c#

I have a simple contact form built in MVC that uses the Html helper class to generate the textbox and drop down. I'd like to clear the values of the textboxes and drop down list to as it was when the page was rendered using a get (only after the inquiry was submitted properly).
I am using the method ModelState.Clear() to perform this clean up but my form values still remain, any idea on what I am doing wrong here? Upon success it does display the message in the code. Below you will find a copy of the code from my controller.
Thanks for taking the time!
[HttpPost]
public ActionResult Contact(ContactUsViewModel model)
{
if (ModelState.IsValid)
{
bool isSuccess = _siteService.CreateInquiry(model.Inquiry);
if (isSuccess)
{
model.SuccessMessage = "Thank you for contacting us.";
ModelState.Clear();
}
}
model.InquiryTypes = InquiryTypes;
return View(model);
}

In case of success simply redirect to your GET action following the Post-Redirect-Get pattern:
public ActionResult Contact()
{
var model = new ContactUsViewModel
{
SuccessMessage = TempData["SuccessMessage"] as string
};
return View(model);
}
[HttpPost]
public ActionResult Contact(ContactUsViewModel model)
{
if (ModelState.IsValid)
{
bool isSuccess = _siteService.CreateInquiry(model.Inquiry);
if (isSuccess)
{
TempData["SuccessMessage"] = "Thank you for contacting us.";
return RedirectToAction("Contact");
}
}
// since you are modifying the value of the InquiryTypes property
// you need to remove it from the modelstate to avoid getting the
// old value rendered by the helpers
ModelState.Remove("InquiryTypes");
model.InquiryTypes = InquiryTypes;
return View(model);
}
or since I am not a big fan of TempData (because it uses Session and personally I always disable it in my applications), you could simply pass a query string parameter to the Contact GET action like (success=1) and inside this action prepare the success message.

You should construct a new model object beside invoking ModelState.Clear(), such as in
model = new Vacancies();
ModelState.Clear();

That's because ModelState is a property of a Controller, it is not what is holding your submitted form values. Here's what you'd want to do:
if (ModelState.IsValid)
{
bool isSuccess = _siteService.CreateInquiry(model.Inquiry);
if (isSuccess)
{
model = new ContactUsViewModel(); // modified line
model.SuccessMessage = "Thank you for contacting us.";
}
}
model.InquiryTypes = InquiryTypes;
return View(model);
The Controller.ModelState property, as explained by MSDN:
Gets the model state dictionary object that contains the state of the model and of model-binding validation.
All I did in my code example was take out your ModelState.Clear() method call and put in model = new ContactUsViewMode();. That's a haphazard way of clearing your ViewModel data properties.
Alternative Process
You could also do something like this, and change your ViewModel:
public class ContactUsViewModel()
{
// ... all of your existing view model members
public void Clear()
{
this.SomeStringProperty = string.Empty;
this.SomeIntProperty = 0;
// ... so on and so forth
}
}
And then instead of creating a new instance of your ViewModel class, you can merely call Clear() on your existing instance:
if (ModelState.IsValid)
{
bool isSuccess = _siteService.CreateInquiry(model.Inquiry);
if (isSuccess)
{
model.Clear(); // modified line
model.SuccessMessage = "Thank you for contacting us.";
}
}
model.InquiryTypes = InquiryTypes;
return View(model);

This is a bit late in the game but I came up with the following method ClearModelState:
public void ClearModelState()
{
var modelStateKeys = ModelState.Keys;
var formKeys = Request.Form.Keys.Cast<string>();
var allKeys = modelStateKeys.Concat(formKeys).ToList();
var culture = CultureInfo.CurrentUICulture;
foreach (var key in allKeys)
{
ModelState[key] = new ModelState { Value = new ValueProviderResult(null, null, culture) };
}
}

Related

How to pass list of strings to view with action result method in ASP dotNet Core

I have an ActionResult Method which i wanted to return some values to the view for using in a form to submit afterwards.
How can I access these data from view for submition in a form?!
Here is my ActionResult method:
[HttpPost]
public virtual async Task<IActionResult> ImportPhonenumbersFromExcel(IFormFile importexcelfile, int currentFestivalId)
{
if (!await _permissionService.AuthorizeAsync(StandardPermissionProvider.ManageFestivals))
return AccessDeniedView();
try
{
if (importexcelfile != null && importexcelfile.Length > 0)
{
var result = await _importManager.ImportPhonenumbersFromXlsxAsync(importexcelfile.OpenReadStream());
}
else
{
_notificationService.ErrorNotification(await _localizationService.GetResourceAsync("Admin.Common.UploadFile"));
return RedirectToAction("Edit", new { id = currentFestivalId });
}
_notificationService.SuccessNotification(await _localizationService.GetResourceAsync("Admin.Festival.Phonenumbers.Imported"));
return RedirectToAction("Edit", new { id = currentFestivalId });
}
catch (Exception em)
{
await _notificationService.ErrorNotificationAsync(em);
return RedirectToAction("Edit", new { id = currentFestivalId });
}
}
For strongly typed data a view model is the best option. It is a class with properties that can be used to store your specific values which you want to pass to the view. To use a viewmodel:
Create the viewmodel class and add the properties you need.
Instantiate a new viewmodel object in your controller. The example shown below is from the Microsoft documentation where the viewmodel class is named Address.
public IActionResult Contact()
{
ViewData["Message"] = "Your contact page.";
var viewModel = new Address()
{
Name = "Microsoft",
Street = "One Microsoft Way",
City = "Redmond",
State = "WA",
PostalCode = "98052-6399"
};
return View(viewModel);
}
Once you have set the values of the properties of the viewmodel object in the controller you can then add the viewmodel to the view.
To send the viewmodel to the view, pass it as a parameter:
return View(viewModel);
Finally, add to the top of your view file:
#model yourViewModelsAddress
To refer to the properties of your viewmodel in your view, follow this example:
`#Model.Property`
You can create a view model that will contain the list and everything else you want to pass to the view.
Then in the view you can access it by using #model.

Using model to save a list, works first time but second time it saves 62 count item list as 1 list item [duplicate]

I want to know, there is any technique so we can pass Model as a parameter in RedirectToAction
For Example:
public class Student{
public int Id{get;set;}
public string Name{get;set;}
}
Controller
public class StudentController : Controller
{
public ActionResult FillStudent()
{
return View();
}
[HttpPost]
public ActionResult FillStudent(Student student1)
{
return RedirectToAction("GetStudent","Student",new{student=student1});
}
public ActionResult GetStudent(Student student)
{
return View();
}
}
My Question - Can I pass student model in RedirectToAction?
Using TempData
Represents a set of data that persists only from one request to the
next
[HttpPost]
public ActionResult FillStudent(Student student1)
{
TempData["student"]= new Student();
return RedirectToAction("GetStudent","Student");
}
[HttpGet]
public ActionResult GetStudent(Student passedStd)
{
Student std=(Student)TempData["student"];
return View();
}
Alternative way
Pass the data using Query string
return RedirectToAction("GetStudent","Student", new {Name="John", Class="clsz"});
This will generate a GET Request like Student/GetStudent?Name=John & Class=clsz
Ensure the method you want to redirect to is decorated with [HttpGet] as
the above RedirectToAction will issue GET Request with http status
code 302 Found (common way of performing url redirect)
Just call the action no need for redirect to action or the new keyword for model.
[HttpPost]
public ActionResult FillStudent(Student student1)
{
return GetStudent(student1); //this will also work
}
public ActionResult GetStudent(Student student)
{
return View(student);
}
Yes you can pass the model that you have shown using
return RedirectToAction("GetStudent", "Student", student1 );
assuming student1 is an instance of Student
which will generate the following url (assuming your using the default routes and the value of student1 are ID=4 and Name="Amit")
.../Student/GetStudent/4?Name=Amit
Internally the RedirectToAction() method builds a RouteValueDictionary by using the .ToString() value of each property in the model. However, binding will only work if all the properties in the model are simple properties and it fails if any properties are complex objects or collections because the method does not use recursion. If for example, Student contained a property List<string> Subjects, then that property would result in a query string value of
....&Subjects=System.Collections.Generic.List'1[System.String]
and binding would fail and that property would be null
[HttpPost]
public async Task<ActionResult> Capture(string imageData)
{
if (imageData.Length > 0)
{
var imageBytes = Convert.FromBase64String(imageData);
using (var stream = new MemoryStream(imageBytes))
{
var result = (JsonResult)await IdentifyFace(stream);
var serializer = new JavaScriptSerializer();
var faceRecon = serializer.Deserialize<FaceIdentity>(serializer.Serialize(result.Data));
if (faceRecon.Success) return RedirectToAction("Index", "Auth", new { param = serializer.Serialize(result.Data) });
}
}
return Json(new { success = false, responseText = "Der opstod en fejl - Intet billede, manglede data." }, JsonRequestBehavior.AllowGet);
}
// GET: Auth
[HttpGet]
public ActionResult Index(string param)
{
var serializer = new JavaScriptSerializer();
var faceRecon = serializer.Deserialize<FaceIdentity>(param);
return View(faceRecon);
}
[NonAction]
private ActionResult CRUD(someModel entity)
{
try
{
//you business logic here
return View(entity);
}
catch (Exception exp)
{
ModelState.AddModelError("", exp.InnerException.Message);
Response.StatusCode = 350;
return someerrohandilingactionresult(entity, actionType);
}
//Retrun appropriate message or redirect to proper action
return RedirectToAction("Index");
}
i did find something like this, helps get rid of hardcoded tempdata tags
public class AccountController : Controller
{
[HttpGet]
public ActionResult Index(IndexPresentationModel model)
{
return View(model);
}
[HttpPost]
public ActionResult Save(SaveUpdateModel model)
{
// save the information
var presentationModel = new IndexPresentationModel();
presentationModel.Message = model.Message;
return this.RedirectToAction(c => c.Index(presentationModel));
}
}

MVC 5 asp.net, viewbag from controller to view is not working

this is the controller
public ActionResult Test() {
#ViewBag.TheMessageIs = "this is the message";
return RedirectToAction("Details", new { id = theId});
}
on the view of Action Named Details I will check if it has the ViewBag to show and show it:
#{
if(ViewBag.TheMessageIs != null){
#ViewBag.TheMessageIs
}
}
but here the redirection is working fine to the page, it's not show the message I have stored in ViewBag.TheMessageIs
thanks
Basically what you're doing is invoking the method Details from your Index method and since you're already overloading your Details action with an id, pass it the message as well:
public ActionResult Index()
{
//ViewBag.TheMessageIs = "this is the message";
return RedirectToAction("Details", new { id = 1, TheMessageIs = "this is the message" });
}
public ActionResult Details(int id, string TheMessageIs)
{
ViewBag.TheMessageIs = TheMessageIs;
return View();
}
Then in the Details view you can access the property like this:
#ViewBag.TheMessageIs
public ActionResult Test() {
TempData["shortMessage"] = "MyMessage";
return RedirectToAction("Details", new { id = theId});
}
public ActionResult Details {
//now I can populate my ViewBag (if I want to) with the TempData["shortMessage"] content
ViewBag.TheMessageIs = TempData["shortMessage"].ToString();
return View();
}
You have to do it like this since the viewbag looses its value when you redirect to another active / view

ASP.NET MVC: How to pass parameter between controllers before and after post

I have below controllers:
// GET: /MaterialPaymentRequestSbuItem/CreateChild
public ActionResult CreateChild(int? parentId)
{
if (parentId==null)
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
var parenetRequest = (from request in db.MaterialPaymentRequests
where request.Id==(int)parentId
select request);
ViewBag.MaterialPaymentRequestId = new SelectList(parenetRequest, "Id", "Description", (int)parentId);
ViewBag.ParentID = parentId;
if (parenetRequest!=null && parenetRequest.First()!=null)
ViewBag.ParentTitle = parenetRequest.First().Description;
return View();
}
// POST: /MaterialPaymentRequestSbuItem/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 CreateChild([Bind(Include = "Id,Name,Unit,UnitPrice,MaterialPaymentRequestId,Quantity")] MaterialPaymentRequestSubItem materialpaymentrequestsubitem)
{
if (ModelState.IsValid)
{
try
{
db.MaterialPaymentRequestSubItems.Add(materialpaymentrequestsubitem);
db.SaveChanges();
}
catch (Exception e)
{
return new HttpStatusCodeResult(HttpStatusCode.InternalServerError);
}
updateTotalPriceOfParentPaymentRequest(db, db.MaterialPaymentRequests.Find(materialpaymentrequestsubitem.MaterialPaymentRequestId));
return RedirectToAction("List", new { id = materialpaymentrequestsubitem.MaterialPaymentRequestId });
}
ViewBag.MaterialPaymentRequestId = new SelectList(db.PaymentRequests, "Id", "Description", materialpaymentrequestsubitem.MaterialPaymentRequestId);
//need to becheked
ViewBag.ParentID = materialpaymentrequestsubitem.MaterialPaymentRequestId;
if (Request != null && Request["parentID"] != null)
{
try
{
int id = Int32.Parse(Request["parentID"]);
ViewBag.ParentTitle = db.MaterialPaymentRequests.Find(id).Description;
}
catch(Exception e)
{
}
}
return View(materialpaymentrequestsubitem);
}
My main problem is that user is allowed to select model.MaterialPaymentRequestId from drop-down and he/she may leave it with no value selected. MaterialPaymentRequestId is used in first controller (befor post) to find Parent title from db and pass it to view using ViewBag, however if user does not select MaterialPaymentRequestId dropdown items, after postback, I lost MaterialPaymentRequestId. Currently I read Request variable and look inside url to find parameters to lookup for parentID.
My url calls are like http://localhost:46813/Admin/MaterialPaymentRequestSbuItem/CreateChild?parentId=23.
However this practice seems like a bad practice than I cannot pass v variable between two controller methods a process like this:
Controller method(get) ---> View ---> Controller method(post)
Currently I feel a bit stuck in MVC!
i hope i got your point right, you can use an #Html.hiddenfor(model => model.ParentId) in your view it will store ParentId value from query string and when user submits form will be posted to POST method so you dont need to look into url to get it. use a viewmodel with ParentId property and do as below
public class MaterialPaymentsViewModel {
//other properties
public int ParentId {get;set;}
}
public ActionResult CreateChild(int? parentId)
{
var model = new MaterialPaymentsViewModel{ParentId = parentId};
//other stuff
return View(model);
}
//View
#using (Html.beginform()){
//
#html.hiddenfor(m => m.ParentId)
//submit
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreateChild(MaterialPaymentsViewModel model)
{
if(model.ParentId != null)
{
//
}
//
}

MVC ModelState.IsValid=true with a null required property

I have this Model
public class ModelVM
{
private string _rD;
[Required]
public string RD
{
get
{
return _rD;
}
set
{
_rD = RCodes.Contains(value)? value : null;
}
}
private static List<string> RCodes = new List<string>
{
"OK",
"OTHER",
"ANOTHER"
};
}
In my MVC Controller
public class MyController : Controller
{
public ActionResult Index(ModelVM modelVM, FormCollection collection)
{
if (!ModelState.IsValid)
return Json(new
{
Result = "ERROR",
Message = "Missing fields."
});
return Json("OK");
}
}
I send: { RD: "Whatever" }
And in debugging ModelState.IsValid=true. I have a similar code on a WebApi Controller and works as I expect (modelstate.valid=false)
Do you have some ideas why MVC is doing that? or what is wrong with my code?
ModelState.IsValid tells you if any model errors have been added to ModelState.
In this case it is valid because there are no client side errors in the provided data that would affect ModelState.
You said...
I sent { RD: "Whatever" }
...which would mean that the model binder will look at the data sent and match the properties with the intended type. From a model binding perspective the [Required] validation was met because when the binder looked at the route value dictionary for the required property RD, it was provided by the client in the incoming data.
If you want to manually invalidate the state you can...
public ActionResult Index(ModelVM modelVM, FormCollection collection)
{
if(ModelState.IsValid) {
if(modelVM.RD == null) {
ModelState.AddModelError("RD", "RD is invalid.");
}
}
if (!ModelState.IsValid)
return Json(new
{
Result = "ERROR",
Message = "Missing fields."
});
return Json("OK");
}

Categories

Resources