This question already has answers here:
Textbox reverts to old value while Modelstate is valid on postback
(2 answers)
Closed 2 years ago.
Suppose i have a model with a validation
public class LoginModel
{
[Required(ErrorMessage="ID not entered")]
[StringLength(5,ErrorMessage = "Length should be 5")]
public string Id { get; set; }
}
In the view page i have a text box. When the validation fails. The controller returns to the view page with error message.
public ActionResult Login()
{
return View();
}
[HttpPost]
public ActionResult Login(LoginModel lg)
{
if(ModelState.IsValid)
{
return RedirectToAction("Index", "Home");
}
return View();
}
But the text box contains the previous values. How does it retains the value? And is there any way to avoid it?
Thanks
to delete the data just try the link which rattlemouse has posted -
ModelState.Clear()
or you do it manually with
[HttpPost]
public ActionResult Login(LoginModel lg)
{
if(ModelState.IsValid)
{
return RedirectToAction("Index", "Home");
}
else{
lg.Id = string.Empty;
return View(lg);
}
}
Clear ModelState and return view without model.
ModelState.Clear();
return View();
ModelState
Controller.ModelState Property
ModelState is a property of a Controller, and can be accessed from those classes that inherit from System.Web.Mvc.Controller. The ModelState represents a collection of name and value pairs that were submitted to the server during a POST. It also contains a collection of error messages for each value submitted.
Related
I'm new to MVC and trying to create a wizard-style series of views, passing the same model instance from one view to the next, where the user completes a little more information on each form. The controller looks something like this:-
[HttpGet]
public ActionResult Step1()
{
return View();
}
[HttpPost]
public ActionResult Step1(MyModel model)
{
if (!ModelState.IsValid)
return View(model);
return View("Step2", model);
}
[HttpPost]
public ActionResult Step2(MyModel model)
{
if (!ModelState.IsValid)
return View(model);
return View("Step3", model);
}
// etc..
Questions:-
When I submit the form from the Step1 view, it calls the Step1 POST method and results in the Step2 view being displayed in the browser. When I submit the form on this view, it calls the Step1 POST method again! I got it to work by specifying the action and controller name in Html.BeginForm(), so I'm guessing that the parameterless overload just POSTs back to the action that rendered the view?
I've noticed that the browser's address bar is out of sync with the current view - when I'm on the Step2 view it still shows the Step1 URL, and when on Step3 it shows the Step2 URL. What's going on?
Another approach I've seen for passing a model between views is to put the model in TempData then use RedirectToAction(). What are the pros and cons of this method versus what I'm currently doing?
I won't be providing any "back" buttons of my own in the wizard. Are there any pitfalls to be aware of regarding the browser's back button, and do either of the above two approaches help (or hinder)?
Edit
Prompted by #StephenMuecke's comment I've now rewritten this to use a single view. I tried this once before but had difficulties round-tripping a "step number" to keep track of where I was in the wizard. I was originally using a hidden field created with #Html.HiddenFor', but this wasn't updating as the underlying model property changed. This appears to be "by design", and the workaround is to create the hidden field using vanilla HTML (
Anyway the one-view wizard is now working. The only problem is the old chestnut of the user being able to click the back button after they have completed the wizard, make a change, and resubmit a second time (resulting in a second DB record).
I've tried adding [OutputCache(NoStore = true, Duration = 0, VaryByParam = "None")] to my POST method, but all this does is display (in my case) a Chrome error page suggesting that the user clicks refresh to resubmit the form. This isn't user friendly and doesn't prevent a second submit.
you can use RedirectToAction() in this case without worrying about TempData. Just add your model as a parameter to each action and use RedirectToAction("Step2", model);
[HttpGet]
public ActionResult Step1()
{
return View();
}
[HttpPost]
public ActionResult Step1(MyModel model)
{
if (!ModelState.IsValid)
return View(model);
return RedirectToAction("Step2", model);
}
[HttpGet]
public ActionResult Step2(MyModel model)
{
return View(model);
}
[HttpPost]
public ActionResult Step2(MyModel model)
{
if (!ModelState.IsValid)
return View(model);
return RedirectToAction("Step3", model);
}
// etc..
The answer to #1 is found in #2.. if you dont specify the Action in you Html.BeginForm() it posts to the current url.
Using TempData to avoid model displaying in url.
[HttpGet]
public ActionResult Step1()
{
return View();
}
[HttpPost]
public ActionResult Step1(MyModel model)
{
if (!ModelState.IsValid)
return View(model);
TempData["myModel"] = model;
return RedirectToAction("Step2");
}
[HttpGet]
public ActionResult Step2()
{
var model = TempData["myModel"] as MyModel;
return View(model);
}
[HttpPost]
public ActionResult Step2(MyModel model)
{
if (!ModelState.IsValid)
return View(model);
TempData["myModel"] = model;
return RedirectToAction("Step3");
}
// etc..
Another option would be to add the name of the next action to ViewBag and set your actionName in each BeginForm()
[HttpGet]
public ActionResult Step1()
{
ViewBag.NextStep = "Step1";
return View();
}
[HttpPost]
public ActionResult Step1(MyModel model)
{
if (!ModelState.IsValid)
{
ViewBag.NextStep = "Step1";
return View(model);
}
ViewBag.NextStep = "Step2";
return View("Step2", model);
}
[HttpPost]
public ActionResult Step2(MyModel model)
{
if (!ModelState.IsValid)
{
ViewBag.NextStep = "Step2";
return View(model);
}
ViewBag.NextStep = "Step3";
return View("Step3", model);
}
//View
#using (Html.BeginForm((string)ViewBag.NextStep, "ControllerName"))
{
}
I'd prefer to add NextStep as a property to MyModel and using that instead of using ViewBag though.
I understand the thought behind your approach and don't have any issues with it. Unfortunately, I don't believe that ASP.NET MVC is geared very well for passing the the same view model (with data!) between different actions.
Typically, the scaffolded actions in the controller will either create a model item or find it by identifier in the database.
I don't know if this would help, but you could try to save it to the database on every step, and then retrieve it by identifier, or you could also save it to a session and grab it that way.
One issue I do see with your approach is you have Step2 set as a get, yet you probably want to post data to it from Step1 instead of using a query string. You may need to reconcile that issue.
I have the following snippet of asp.net mvc controller code that check if state is invalid, then I will update the value of one field:
[HttpPost]
public ActionResult Create(ContactInfo contactinfo)
{
if (IsModelStateValid(GetIssues(contactinfo)))
{
db.ContactInfoes.Add(contactinfo);
db.SaveChanges();
return RedirectToAction("Index");
}
contactinfo.Name+="why this is not working".
return View(contactinfo);
}
Through debugging I checked that the new value of Name field is successfully passed to Model of my View, but in the render result, only the field validation fields are updated, the field value change is not rendered, Could someone help me on how to apply this change?
You got sort of cache problem, clear it with:
[HttpPost]
public ActionResult Create(ContactInfo contactinfo)
{
if (IsModelStateValid(GetIssues(contactinfo)))
{
db.ContactInfoes.Add(contactinfo);
db.SaveChanges();
return RedirectToAction("Index");
}
// Clear the model state.
ModelState.Clear(); // <-----------------------------------------------
// Or just remove the `Name` property:
ModelState.Remove("Name")
contactinfo.Name+="why this is not working".
return View(contactinfo);
}
The standard generated code bock for responding to the Edit save form
[HttpPost]
public ActionResult Edit(User user)
{
if (ModelState.IsValid)
{
db.Entry(user).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(user);
}
works when all fields are present in the edit view.
In my model I have a password field that (for obvious reasons) I don't want to pass as an invisible form field.
As a result password is null (which is to be expected).
How should I code to handle this scenario?
get the password value, should I fetch the value from the database?
if I use: User u = db.User.Find(user.ID); EF has issues with "an object with the same key already exists in the ObjectStateManager"
how do I tackle the ModelState.IsValid as it is false
I have found answers for previous mvc versions, but was wondering what is the most elegant / efficient way of achieving this?
#freeride, surly I don't have to test if user.Password is null, I expect it to be null and if it isn't then it was injected by a malicious user. The entire excersie was to avoid user manipulation of the password.
Removing the password field (ModelState.Remove("Password");) does solve the ModelState validation issue.
Now remains the question, how do I restore the password value? Following works:
[HttpPost]
public ActionResult Edit(user user)
{
user v = db.Users.Find(user.ID);
ModelState.Remove("Pass");
user.Password = v.Password; // assign from db
if (ModelState.IsValid)
{
//db.Entry(user).State = EntityState.Modified; // this won't work as the context changed
db.Entry(v).CurrentValues.SetValues(user); // we need to use this now
db.SaveChanges();
return RedirectToAction("Index");
}
return View(user);
}
I wonder if I'm obliged to do an extra db trip to fetch the value? I had to change the code to fix the EF context change, it works, but is the code right?
Before
if (ModelState.IsValid)
add
if (string.IsNullOrEmpty(user.Password))
{
ModelState.Remove("Password");
}
I wonder if I'm obliged to do an extra db trip to fetch the value? I
had to change the code to fix the EF context change, it works, but is
the code right?
Do it in a different way. Don't use an entity object as your model.
Create a model which contains only data you need to update,for example:
public class UserModel
{
public int Id { get; set; }
public string FirstName { get; set; }
public string Surname { get; set; }
}
and now:
[HttpPost]
public ActionResult Edit(UserModel userModel)
{
if (ModelState.IsValid)
{
User user = db.Users.Find(userModel.Id);
user.FirstName = userModel.FirstName ;
user.Surname = userModel.Surname;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(userModel);
}
I hope it helps.
I'm passing in some values to my controller action and everything is binding up fine. There will be two properties missing from the form POST by design.
I am then setting the missing values but then I want to validate the model and it is still saying false since it looks like the ModelState hasn't caught up with my changes.
[HttpPost, Authorize]
public ActionResult Thread(int id, string groupSlug, Comment comment, string submitButton)
{
comment.UserID = UserService.UID;
comment.IP = Request.UserHostAddress;
UpdateModel(comment); //throws invalidoperationexception
if (ModelState.IsValid) // returns false if i skip last line
{
//save and stuff
//redirect
}
//return view
}
What is the cleanest way to pat the ModelState on the head and tell it that everything will be okay whilst still validating everything else that was bound from the user's POST
If the missing Values are required for your model but will not be provided until after binding you may need to clear the errors caused by those two values from the ModelState.
[HttpPost, Authorize]
public ActionResult Thread(int id, string groupSlug, Comment comment, string submitButton)
{
comment.UserID = UserService.UID;
comment.IP = Request.UserHostAddress;
//add these two lines
ModelState["comment.UserID"].Errors.Clear();
ModelState["comment.IP"].Errors.Clear();
UpdateModel(comment); //throws invalidoperationexception
if (ModelState.IsValid) // returns false if i skip last line
{
//save and stuff
//redirect
}
//return view
}
I'm using the ASP.NET Core 1.0.0 and async binding and for me the solution was to use ModelState.Remove and pass the property name (without object name).
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Submit([Bind("AerodromeID,ObservationTimestamp,RawObservation")] WeatherObservation weatherObservation)
{
weatherObservation.SubmitterID = this.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
weatherObservation.RecordTimestamp = DateTime.Now;
ModelState.Remove("SubmitterID");
if (ModelState.IsValid)
{
_context.Add(weatherObservation);
await _context.SaveChangesAsync();
return RedirectToAction("Index", "Aerodrome");
}
return View(weatherObservation);
}
Before .NET Core you can use Validate(TEntity entity) function of controller. But first you have to clear existing ModelState errors.
Suppose you set a missing required property to entity.
ModelState.Clear();
Validate(entity);
if (!ModelState.IsValid) {}
With Core you can use TryValidateModel instead of ModelState.IsValid
see thiis: https://learn.microsoft.com/en-us/aspnet/core/mvc/models/validation?view=aspnetcore-5.0
I have a view model with 2 properties that are optional - ie - not required. The view uses dropdownlistfor() to get values for these two fields, an includes an optionlabel of "" for the blank value.
When posted back to the create action the ModelState has an error for both of these fields saying "A value is required".
Anyone got any clue if this is a bug or a stupid user (ie, me) error?
Thanks
Udpate:
The View Model looks like this:
[DisplayName("Check Digit Type")]
public VMBarcodeMaskCheckDigitType BarcodeMaskCheckDigitType
{
get;
set;
}
[DisplayName("Mask Type")]
[Required(ErrorMessage="Mask type is required")]
public VMBarcodeMaskType BarcodeMaskType
{
get;
set;
}
[DisplayName("Product")]
public VMProduct Product
{
get;
set;
}
The binding in the controller is :
public ActionResult Create()
{
BarcodeMaskViewModel model = new BarcodeMaskViewModel(new VMBarcodeMask(), Domain.GetBarcodeMaskTypes(), Domain.GetBarcodeCheckDigitTypes(), Domain.GetProducts());
return View(model);
}
//
// POST: /Barcode/Create
[HttpPost]
public ActionResult Create(BarcodeMaskViewModel model)
{
try
{
if (ModelState.IsValid)
{
...
}
}
catch (Exception ex)
{
ModelState.AddModelError("*", ex);
}
return View(new BarcodeMaskViewModel(model.BarcodeMask, Domain.GetBarcodeMaskTypes(), Domain.GetBarcodeCheckDigitTypes(), Domain.GetProducts()));
}
I've had this problem too, and I discovered it was actually nothing to do with the optional fields.
It was because I had an auto-generating primary key column for the entity, called 'Id'. MVC2 automatically checked for a value for this, and obviously there wasn't one as it was being auto-generated.
There's an easy way to fix this is to rename the column to BarcodeId etc, rather than just Id. I gave a better explanation here: http://www.ediblecode.com/post/A-value-is-required-with-ASPNET-MVC-2.aspx
That explanation is all assuming you're using LINQ...
Just use Bind(Exclude="Id") before the first parameter of your Create action.
I think this a confirmed bug. See here: http://forums.asp.net/p/1529205/3699143.aspx