Partial Views & ModelState.AddModelError - asp.net-mvc

the source of LoginRegister view is like this :
#Html.Partial("authentication/_login")
#Html.Partial("authentication/_register")
and each child view has got a form with this syntax
#using (Html.BeginForm(**seperated-methods**, "Login"))
{
#Html.ValidationSummary(false)
}
I send error(s) in postback whit this code
ModelState.AddModelError("", "**any-error-message**");
return View("authentication/LoginRegister", customized-data);
The point is , error message shows in both partial views.

You need to tell the ModelState to which property this error refer to:
ModelState.AddModelError("PropertyName", "**any-error-message**");
Now it will be only in the
#Html.ValidationMessageFor(m => m.PropertyName)
If you don't specify the property name, the error will be considered global and get shown in every ValidationSummary.

Related

Changes to VM after POST not reflected in the Page [duplicate]

I want to send a message to userID=3 by going to /MyController/Message/3
This executes Message() [get] action, I enter some text in the text area and click on Save to post the form
Message() [post] action saves the changes, resets the value of SomeText to empty string and returns to the view.
At this point I expect the text area to be empty because I have set ViewData["SomeText"] to string.Empty.
Why is text area value not updated to empty string after post action?
Here are the actions:
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Message(int ID)
{
ViewData["ID"] = ID;
return View();
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Message(int ID, string SomeText)
{
// save Text to database
SaveToDB(ID, SomeText);
// set the value of SomeText to empty and return to view
ViewData["SomeText"] = string.Empty;
return View();
}
And the corresponding view:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
<% using (Html.BeginForm())
{ %>
<%= Html.Hidden("ID", ViewData["ID"])%>
<label for="SomeText">SomeText:</label>
<%= Html.TextArea("SomeText", ViewData["SomeText"]) %>
<input type="submit" value="Save" />
<% } %>
</asp:Content>
The problem is that your ModelState is re-filled with the posted values.
What you can do is clear it on the Action that has the Post attribute :
ModelState.Clear();
The problem is the HtmlHelper is retrieving the ModelState value, which is filled with the posted data. Rather than hacking round this by resetting the ModelState, why not redirect back to the [get] action. The [post] action could also set a temporary status message like this:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Message(int ID, string SomeText)
{
// save Text to database
SaveToDB(ID, SomeText);
TempData["message"] = "Message sent";
return RedirectToAction("Message");
}
This seems to me like more correct behaviour.
The html helpers read the value from the ModelState. And there's no elegant way to override this behaviour.
But if you add this line after SaveToDB(ID, SomeText), it should work :
ModelState["SomeText"].Value =
new ValueProviderResult("", "", CultureInfo.CurrentCulture);
I tried everything, but only worked when I did something like this:
ModelState.Clear();
//This will clear the address that was submited
viewModel.Address = new Address();
viewModel.Message = "Dados salvos com sucesso!";
return View("Addresses", ReturnViewModel(viewModel));
Hope this helps.
Instead of using ModelState.Clear() which clears the whole modelstate, you can do ModelState.Remove("SomeText"), if you want to. Or render the Input without the htmlhelper-extensions.
They are designed to take the Value from ModelState instead of the Model (or viewdata).
That is a clientside behavior. I would recommend using javascript. If you use JQuery, you can do it like this:
<script type="text/javascript">
$(function(){ $("#SomeText").val("");});
</script>
I don't use Javascript anymore, but I believe in regular JS that it is like:
document.getElementById("SomeText").value = "";
(You would do this on one of the load events.
<body onload="...">
Hope this helps.
I am fairly certain the textarea is grabbing the value from the Request.Form under the hood since ViewData["SomeText"] is empty.
Is it possible that the model state has been updated with an error? I believe that it will pull the attempted value from the model state rather than from view data or the model if the model state isn't valid.
EDIT:
I'm including the relevant section of the source code from the TextArea HtmlHelper extension below. It appears to me that it does exactly what I expected -- if there has been a model error, it pulls the value from the model state, otherwise it uses it from ViewData. Note that in your Post method the "SomeText" key shouldn't even exist until you set it, i.e., it won't be carried forward from the version of the code that responds to the GET.
Since you explicitly supply a value to the ViewData, useViewData should be false, attemptedValue should be false unless an error has been set in the model state.
// If there are any errors for a named field, we add the css attribute.
ModelState modelState;
if (htmlHelper.ViewData.ModelState.TryGetValue(name, out modelState)) {
if (modelState.Errors.Count > 0) {
tagBuilder.AddCssClass(HtmlHelper.ValidationInputCssClassName);
}
}
// The first newline is always trimmed when a TextArea is rendered, so we add an extra one
// in case the value being rendered is something like "\r\nHello".
// The attempted value receives precedence over the explicitly supplied value parameter.
string attemptedValue = (string)htmlHelper.GetModelStateValue(name, typeof(string));
tagBuilder.SetInnerText(Environment.NewLine + (attemptedValue ?? ((useViewData) ? htmlHelper.EvalString(name) : value)));
return tagBuilder.ToString(TagRenderMode.Normal);
Do s.th. like this:
add:
ModelState.Clear();
before the return statement of the submit buttons action method. Works for me. It could work for you.

Unable to display validation in partial view

I am using the code from the below link. It works fine . But though there are validation errors, and ValidationSummary(true/false) is enabled, I am not able to see the validation message.
ASP.NET MVC Partial view ajax post?
As a work around ,When I try to display error message explicitly using the below code, it still does not display .When in debug mode, the ModelState has the error message , but it does not display .
<div>
#{
foreach (var i in ViewData.ModelState.Values)
{
<ol>
#{
if(i.Errors != null && i.Errors.Count>0)
{
var error = i.Errors[0];
<li>error.ErrorMessage</li>
}
}
</ol>
}
}
</div>
Please help.
You can use #Html.ValidationMessage html helper to display property-level validation message in your partial view.

Returning a GET view after a POST view Fails ASP.NET MVC

I have a View in MVC called Action, which accepts and ID as parameter. I also have another view called Action, but it is marked as HTTPPOST.
The POST version of Action can fail programatically. When it fails, I want to add a ModelError, and then Redirect to the GET version of Action, whilst I provide the ID .
In short => I want to transfer to a GET view from a POST view, and pass parameters. Something along the lines of
ModelState.AddModelError("", "Could not verify token");
return RedirectToAction("Action", "ExpiredAccounts" new { aid = myId });
Where Action is the View, ExpiredAccounts is the Controller and AID is the Account ID. This of course does not work since you can add a model error to a view, not redirecting
Thanks
You'd better return the same view in this case instead of redirecting:
ModelState.AddModelError("", "Could not verify token");
var model = repository.Get(myId);
return View(model);
The correct flow of the Redirect-After-Post pattern is the following:
GET request -> some form is displayed
POST request -> the form is submitted to the server. Two possible cases here:
Validation succeeds => Redirect.
Validation fails => redisplay the same view so that the user can fix the errors
If you want to violate this best practice you could always send the error message as query string parameter when redirecting:
return RedirectToAction(
"Action",
"ExpiredAccounts"
new { aid = myId, error = "Could not verify token" }
);
and then in side the target action verify whether this parameter has been supplied and add an error to the modelstate:
public ActionResult Action(int myId, string error)
{
if (!string.IsNullOrEmpty(error))
{
ModelState.AddModelError("", error);
}
...
}

How to remove list from validation summary

I added ValidationSummary Html helper for my View Model class which has 5 required fields. And it works got nice red words missing 1, missing 2 etc.
But I need to display just one message not five of them (something like: "Your input is not valid."). Can this be done with ValidationSummary?
You have two options (at least):
Either use the validation summary and exclude property errors:
#Html.ValidationSummary(true, "The input is not valid")
or associate a error message with a custom key in your action:
if (!ModelState.IsValid)
{
ModelState.AddModelError("myerrorsummary", "The input is not valid");
}
and display it on your page:
#Html.ValidationMessage("myerrorsummary")
You can try skipping the helpers if all you want to do is simply display a message if the ModelState is not valid. Simply check the ModelState within ViewData and that should work.
#if (!ViewData.ModelState.IsValid)
{
<p>Your input is not valid.</p>
}
If you look at MVC3 source code you'll see that, currently, if you use ValidationSummary with excludePropertyErrors=true while having UnobtrusiveJavaScriptEnabled, there won't be any validation summary rendered.
I was able to display just a single message with MVC3 with UnobtrusiveJavascript enabled, for client side validation. Don't use #Html.ValidationSummary at all, and render:
#{
//Show the message when there are server side errors
ViewBag.ValidationSummaryClass = ViewData.ModelState.IsValid ? "validation-summary-valid" : "validation-summary-errors";
}
<div class="#ViewBag.ValidationSummaryClass" data-valmsg-summary="true">
<span>Before you can continue, please make sure you have completed the mandatory fields highlighted above.</span>
<ul style="display:none"/>
</div>
Notice the display:none, unobtrusive javascript still fills the list with error messages, but they are kept hidden.
One brute force approach I've used in MVC3:
if (!ModelState.IsValid)
{
ModelState.AddModelError("", "Some contextual error message");
}
and display it on your page:
<% if(!ViewData.ModelState.IsValid) { %>
<span class="error"><%=ViewData.ModelState[String.Empty].Errors[0].ErrorMessage %> </span>
<% } %>

ASP.Net MVC Custom Error handling via Action Filter Attributes

I am trying to implement Custom Error handling via Action Filter Attributes.
My code is as follows:
[HandleError (Order = 2)]
[HandleError (Order = 1, ExceptionType = typeof(NullReferenceException), View = "CustomError")]
public class ArticlesController : Controller
{
public object OhDearACrash()
{
throw new Exception("Oh Dear");
}
public ActionResult NullRefCrash()
{
throw new NullReferenceException();
}
public ActionResult ThrowNotImplemented()
{
throw new NotImplementedException();
}
OhDearACrash and ThrowNotImplemented are both picked up by [HandleError] which renders the error message via Error.aspx located in Views/Shared.
For example with OhDearACrash:
Message = <%= ViewData.Model.Exception.Message %>
renders
Message = Oh Dear
NullRefCrash is picked up by the [HandeError] that deals with an ExceptionType = typeof(NullReferenceException).
When CustomError attempts to render the error message using
Message = <%= ViewData.Model.Exception.Message %>
the ViewData.Model is null and an exception is raised
System.NullReferenceException was unhandled by user code
Message="Object reference not set to an instance of an object."
To create CustomError.aspx I copied Error.aspx and pasted to my newly created Views/Error and renamed to CustomView.aspx.
Since Error.aspx and CustomError.aspx are essentially the same how is this occuring?
Edit:
I created a Test Project that only contains the above and the CustomError.aspx view works perfectly fine - Is there anyway to debug my existing project to find the issue?
I just tried this on ASP.NET MVC 1.0 and I'm getting the correct behavior.
Is it possible that you have another filter somewhere else that is running and changing the error somehow?

Resources