MVC Html.ValidationMessage not firing on update - asp.net-mvc

I'm having an issue where my validation messages are showing up fine on an add operation, but when it comes to the update page, the validation messages are not showing:
This is my action, IsValid is coming out as false, and action redirects to the edit view, but none of the validation messages are shown. Is there something wrong in my approach?
[Authorize]
public ActionResult UpdateCar(CarDTO car)
{
try
{
_carTask.Update(car); //required Name field not set
}
catch (RulesException ex)
{
ex.AddModelStateErrors(ModelState, null);
}
if (!ModelState.IsValid)
{
return RedirectToAction(ViewNames.EditCar, new {carKey = car.carKey});
}
return RedirectToAction(ViewNames.Home, new {carKey = car.carKey});
}
<li>
<label for="Name">Car Name:</label>
<%= Html.TextBoxFor(x => x.Name, new { watermark="Car Name" })%>
<br />
<%= Html.ValidationMessage("Name") %>
</li>

If the form is invalid then you are redirecting to a new page which will loose any modal error values you set. Instead just return the View. Haven't checked the syntax but something like the below.
if (!ModelState.IsValid)
{
return View(ViewNames.EditCar, new {carKey = car.carKey});
}
return RedirectToAction(ViewNames.Home, new {carKey = car.carKey});

Related

Display Alert Message: On OK button click RedirectToAction

I am trying to display a successful/unsuccesful message after attempting a stored procedure. My Save ActionResult is called on Save button click - however before I redirect back to the index I want to display a message box. Is there a way to return a view first, and then RedirectToAction?
Save button points to:
[HttpPost]
public ActionResult Save(string submit, Models.KnownIssues knownIssue)
{
UpdateKnownIssue(knownIssue);
InsertKnownIssue(knownIssue);
return RedirectToAction("Index");
}
The viewbag alerts:
public ActionResult Edit(KnownIssues knownIssue, string submit)
{
if (UpdateKnownIssue(knownIssue))
{
ViewBag.ShowAlert = "<script>alert('Successfully Edited'); window.location.href = '/KnownIssues';</script>";
} else
{
ViewBag.ShowAlert = "<script>alert('Unseccessful. Try again.');</script>";
}
return View(knownIssue);
}
Try this:
return RedirectToAction("Index", "Home", new { ac = "success" });
In the index view do (NB: Am using bootstrap alerts):
#{
var parameter = Request.QueryString["ac"];
//Check parameter here and display Message
if (parameter == "success")
{
<div class="alert alert-success alert-dismissable">
×
<strong><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> Record Added Successfully.</strong>
</div>
}
}
EDIT:
A better approach may be to use TempData, especially if you do not want a variable in the url.
You can set the TempData in the controller like:
TempData["SuccessMessage"] = "Your Success Message";
And in the view display the message by checking if there is anything in the TempData:
#if (TempData["SuccessMessage"] != null)
{
<div class="alert alert-success alert-dismissable">
<strong>#TempData["SuccessMessage"]</strong>
</div>
}
You can convert your form submit to be an ajax form submit and return a JSON response back from your action method. In your ajax call's success event, you can show the message to user and then do a redirect using javascript.
You can use the below jQuery code to ajaxify your form submit.
$(function() {
$("form").submit(function(e) {
e.preventDefault();
var f = $(this);
$.post(f.attr("action"), f.serialize(),function(res) {
if(res.Status==="success")
{
alert(res.Message);
window.location.href="your new url here";
}
});
});
});
and have your action method updated to return json response
[HttpPost]
public ActionResult Save(string submit, Models.KnownIssues knownIssue)
{
// your code
if (Request.IsAjaxRequest())
{
return Json(new {Status = "success", Message="Succesfully updated"});
}
return RedirectToAction("Index");
}
Another option is to Show the message on the next page. For this, you can use your current form submit approach (no need of ajax) and use TempData to store the message. In the next action method, read the TempData in the view and show the message to user. Take a look at the below post for sample code
Display message in a toastr after controller method finish

How to return a single string to a div in my current page from HTTP POST in ASP.NET MVC 5?

I'm trying to post a message after a contact form, indicating to the user that their message has been sent after they click the submit button. I don't want to redirect to a different page or to return a different view inside my HTTP Post action method. How do I do something like that in ASP.NET MVC framework?
Below is my code sample:
#*contactus.cshtml*#
#model MySite.Models.ContactModel
#using (Html.BeginForm())
{
<div class="col-md-6">
<div class="form-group">
#Html.TextBoxFor(model => model.Name})
<p>#Html.ValidationMessageFor(model => model.Name)</p>
</div>
<div class="form-group">
#Html.TextBoxFor(model => model.Email)
<p>#Html.ValidationMessageFor(model => model.Email)</p>
</div>
<div class="form-group">
#Html.TextAreaFor(model => model.Message)
<p>#Html.ValidationMessageFor(model => model.Message)</p>
</div>
<div class="col-lg-12">
<button type="submit">Send Message</button>
</div>
</div>
}
#*ContactModel.cs*#
public class ContactModel
{
[Required(ErrorMessage = "* Please enter your name.")]
[StringLength(100, MinimumLength=3, ErrorMessage="* Please enter your full name.")]
public string Name { get; set; }
[Required]
[EmailAddress(ErrorMessage="* Not a valid email address.")]
public string Email { get; set; }
[Required]
public string Message { get; set; }
}
I only have a contact us form right now on my home/index page, and I don't want to redirect it to any other pages. I would like to display a message right below the Send Message button, but I'm not sure how to go about it using the action method below:
#*HomeController.cs*#
public ActionResult Index(ContactModel model)
{
if (ModelState.IsValid)
{
// this is my helper library, for brevity, I'm not copying it.
EmailHelper emailService = new EmailHelper();
bool success = emailService.SendEmail(model.Name, model.Email, model.Message);
return Content(success ? "success" : "no...something went wrong :(");
} else {
return View(model);
}
}
Right now this controller will return the string inside Content which replaces my entire page, and I would like the string to be returned below my contact form. Also, I have two sections on the same html page with Contact Form as the second one, when I return View(model), it automatically redirects to the first section, which isn't ideal... How do I tell the controller to only redirect it to the second section after the POST method? In addition, I feel like it would be more efficient if it didn't return the whole page... so is there a way to only return a Message string to the div?
You can place a hidden div on the page which will contain the message.
Then when your form has been submitted, capture the click event for your button, and use that to display the hidden message.
Let me know if you need a code example. Posting your form would help us answer you more specifically.
To only show the success message if the form is successfully sent, I would recommend setting a value in the ViewBag in the POST action of the controller and then returning that same page if you want to still have the same page showing. On the View itself, you could then place an If statement to test if the ViewBag variable contains a value and if so, display the message.
Controller:
[HttpPost]
public ActionResult YourAction(YourModel m)
{
//Do stuff to send the contact form
...
if(error)
{
ViewBag.Message = "There was a problem sending the form.";
}
else
{
ViewBag.Message = "The form was sent successfully!";
}
return View(m);
}
View:
#if(ViewBag.Message != null)
{
<div>#ViewBag.Message</div>
}
This lets you check if the form was posted successfully on the server before telling the user the result and will only display a message if ViewBag.Message has been set. Note that you can have as many ViewBag variables as you want and can name them whatever you want... just remember which one you use in which place.
EDIT:
Following the comments, this could also be done using an AJAX call. I'll use the jQuery .post() method for simplicity sake.
In Script:
<script>
$(document).on('click', "#buttonId", function() {
var nameText = $("#IdOfNameField").val();
var emailText = $("#IdOfEmailField").val();
var messageText = $("#IdOfMessageField").val();
$.post('#Url.Content("~/Controller/AJAXPostContactForm")',//the url to post to
{name: nameText, email: emailText, message: messageText }, //these are values to be sent to the action
function(){ //this is the success function
$("#successMessage").val("Form posted successfully.");
}
)
.fail(function() {//failure function
alert("Something went wrong.");
});
}
</script>
Controller:
public void AJAXPostContactForm(string name, string email, string message)
{
try
{
//do stuff with the information passed into the action
}
catch(exception e)
{
//Handle errors. If error thrown, Ajax should hit fail block in script
}
finally
{
//do any cleanup actions
}
}
View:
<div id="successMessage"></div>
I have not tested this code but it should theoretically work. On a specific button click, it will get the values from the form fields, post those values to a specialized ActionResult in the controller, and then return a message about what happened.

Model after post not change why

I have Page for Both Insert User and this work fine but after I insert new info i send new model but this not work.the info that i insert before are still in textbox without any error. why???
return View(new User());
#using (#Html.BeginForm("RegisterUser", "UserManagement", FormMethod.Post))
{
#Html.AntiForgeryToken()
<table class="Registertbl">
<tr>
<td>نام*</td>
<td> #Html.TextBoxFor(m => m.FName, new { maxlength = 20})<br />
</td>
<td>سمت*</td>
<td>#Html.TextBoxFor(m => m.Post, new { maxlength = 200})</td>
</tr>
</table>
<br />
<input type="submit" value="Insert" class="insertBtn" />
#Html.ActionLink("back", "ViewUserList", "UserManagement")
}
//Controller
[HttpGet]
public ActionResult RegisterUser()
{
return View(new User());
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult RegisterUser(Common.UsersManagement.Entities.User model)
{
SetUserManagement();
var Result = userManagement.RegisterUser(model);
if (Result.Mode == Common.Extensions.ActionResultMode.Successfully)
{
return View(new User());
}
// if not Successfull
return View(model);
}
maf748 is correct, you should Post-Redirect-Get. You can communicate to the GET action method using TempData that a message should be displayed, e.g.
TempData.Message = "User registered.";
return RedirectToAction( "RegisterUser" );
Then in your RegisterUser view you can check if TempData.Message has a value.
However, if after all that you still want do do it your way you could try ModelState.Clear() before returning the new View. The problem this will cause is that if the user refreshes the page in their browser it will send them back to your Post method, prompting them in the browser with that awful "do you want to resubmit your data" message and potentially creating duplicate registrations in your database.
Try redirecting back to the GET RegisterUser.
The reason is: when you submit a form to MVC, all the values in the ModelState (basically the Request values) take precedence over any model you pass to the view. The redirect will give you an empty ViewDataDictionary, so all values will be pulled from the Model you're passing (the new User())
if (Result.Mode == Common.Extensions.ActionResultMode.Successfully)
{
return RedirectToAction("RegisterUser");
}

Why is my Account Register view not working?

I'm having problem getting my view to display. I'm creating a link
#Html.ActionLink("Add as user", "Register", "Account")
but when I click on it, I'm getting this message:
The resource cannot be found.
Description: HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. Please review the following URL and make sure that it is spelled correctly.
Requested URL: /Account/Register
Here's my view:
#model ContactWeb.Models.SimpleUser
#{
ViewBag.Title = "CreateUser";
}
<h2>Create User</h2>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true);
<fieldset>
<legend>Create User</legend>
<div>
#Html.LabelFor(c=>c.Username, "User Name")
#Html.TextBoxFor(c=>c.Username)
#Html.ValidationMessageFor(c=>c.Username)
</div>
<div>
#Html.LabelFor(c=>c.Password, "Password")
#Html.TextBoxFor(c=>c.Password)
#Html.ValidationMessageFor(c=>c.Password)
</div>
<div>
#Html.LabelFor(c=>c.ConfirmPassword, "Confirm Password")
#Html.TextBoxFor(c=>c.ConfirmPassword)
#Html.ValidationMessageFor(c=>c.ConfirmPassword)
</div>
<p>
<input type="submit" value="Register" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "List")
</div>
and my controller is
[HttpPost]
public ActionResult Register(RegisterModel model)
{
if (ModelState.IsValid)
{
// Attempt to register the user
MembershipCreateStatus createStatus;
Membership.CreateUser(model.UserName, model.Password, null, null, null, true, null, out createStatus);
if (createStatus == MembershipCreateStatus.Success)
{
FormsAuthentication.SetAuthCookie(model.UserName, false);
return RedirectToAction("List", "Contact");
}
else
{
ModelState.AddModelError("", "The username or password provided is incorrect.");
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
You do want an overload of the Register method for the initial GET request, but it should not have any parameters:
[HttpGet]
public ActionResult Register()
{
return View(new RegisterModel());
}
[HttpPost]
public ActionResult Register(RegisterModel model)
{
// your existing implementation here that
// checks ModelState and creates the user record
}
This will allow displaying a form with empty/default values when first loading the /Account/Register URL. This will prevent the "already defines a method with the same parameter types" and thus allow the code to compile. Also, I think you will find this advantageous to having just one Register method with no HttpPost attribute because it allows you to have a separate POST-only method to implement the postback logic, and a simpler GET-only method for the initial display. You could even customize the GET-only display to populate the model/view with certain initial values, etc.
Your register action is decorated with [HttpPost] attribute, that means that action can handle only HTTP POST requests. Ordinary links make GET requests, and as there's no handler for GET, you get 404 - not found. To fix this, create another action that will handle GET requests
[HttpGet]
public ActionResult Register()
{
return View();
}
This action will return page, with registration form on it.

Modelstate with Ajax Form

I'm using an AJAX form to update an item to the database. When it gets done, it returns a partial view that re-lists all the items and displays them all in a table. The problem occurs when I have to add a modelstate error in my controller action. I don't want to return the list of items when there is a modelstate error because I want to show the user the error using the ValidationMessage. My thinking is that I could do something like this in my controller:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult UpdateNewsItem(int newsID, string newsTitle, string newsDescription, string newsBeginningDate, string newsEndingDate)
{
List<Models.News> lstNewsItem = new List<News>();
//we need to grab the member so we can capture the user id
//for the corresponding news property
MembershipUser member = Membership.GetUser(User.Identity.Name);
//the news instance to use in case the viewdata is invalid
Models.News newsError = new Models.News();
//create the datetime objects
DateTime dtBeginningDate = DateTime.MinValue;
DateTime dtEndingDate = DateTime.MaxValue;
//the message we want to send whenever the user enters an invalid date
string strInvalidDateError = "Invalid date. Please use a format like '12/25/2008'";
//clean user input
newsTitle = Models.clsGlobals.CleanString(newsTitle);
newsDescription = Models.clsGlobals.CleanParagraph(newsDescription);
//newsTitle
if (string.IsNullOrEmpty(newsTitle))
{
newsError.Title = string.Empty;
ModelState.AddModelError("newsTitle", "You must enter a news title.");
}
//description
if (string.IsNullOrEmpty(newsDescription))
{
newsError.Description = string.Empty;
ModelState.AddModelError("newsDescription", "You must enter a news description.");
}
//beginningDate
if (string.IsNullOrEmpty(newsBeginningDate))
{
ModelState.AddModelError("newsBeginningDate", "You must enter a beginning date.");
}
//endingDate
if (string.IsNullOrEmpty(newsEndingDate))
{
ModelState.AddModelError("newsEndingDate", "You must enter an ending date.");
}
//set the beginning date
try
{
dtBeginningDate = DateTime.Parse(newsBeginningDate);
newsError.BeginningDate = dtBeginningDate;
}
catch (FormatException)
{
ModelState.AddModelError("newsBeginningDate", strInvalidDateError);
}
//set the ending date
try
{
dtEndingDate = DateTime.Parse(newsEndingDate);
newsError.EndingDate = dtEndingDate;
}
catch (FormatException)
{
ModelState.AddModelError("newsEndingDate", strInvalidDateError);
}
//data is validated, so we can begin the update
if (ModelState.IsValid == true)
{
try
{
//use to perform actions on db
Models.NewsDataContext dcNews = new Models.NewsDataContext();
//fetch the items that match what the user requested to edit
lstNewsItem = this.GetNewsItem(newsID);
//set news properties
foreach (Models.News news in lstNewsItem)
{
news.UserId = (Guid)member.ProviderUserKey;
news.Title = newsTitle;
news.Description = newsDescription;
news.EntryDate = DateTime.Now;
news.BeginningDate = dtBeginningDate;
news.EndingDate = dtEndingDate;
}//next
//update the transaction
dcNews.SubmitChanges();
//update the news list
return PartialView("NewsList", this.GetNewsItems());
}
//just to make sure everything goes as planned,
// catch any unhandled exceptions
catch (Exception ex)
{
ModelState.AddModelError("_FORM", ex);
}//end catch
}//end if valid modelstate
//invalid modelstate, so repopulate the viewdata and
//send it back
//the list to hold the entries
List<Models.News> lstErrorNewsItems = new List<Models.News>();
//set the remaining error properties
newsError.UserId = (Guid)member.ProviderUserKey;
newsError.NewsID = newsID;
newsError.EntryDate = DateTime.Now;
//add the item--there will only be one
//but the view is expecting a list so we will
//treat it like one
lstErrorNewsItems.Add(newsError);
return PartialView("EditNews", lstErrorNewsItems);
}//end actionresult
The problem is that when a modelstate error occurs, the modelstate viewdata isn't returned. I suspect it's possibly because I'm not specifying an update target id. But I can't set another updatetargetid because I already have one. Any ideas?
if (!Model.IsValid) {
return PartialView("YourEditForm");
}
else
{
return View("YourIndexView");
}
Should work fine for redisplaying your edit form with validation errors and all.
ViewData gets populated from Post data.
Big Update
I've made some testing to figure it out. And came up with a working test project.
here are some listings:
My Testing controller
public class TestController : Controller
{
//
// GET: /Test/
List<TestData> data;
public TestController()
{
data = new List<TestData>();
for (var i = 0; i < 10; i++)
{
data.Add(new TestData(){Name=string.Format("TestData{0}",i.ToString().PadLeft(4,'0'))});
}
}
public ActionResult Index()
{
return View( data);
}
public ActionResult Edit(string name)
{
if (Request.IsAjaxRequest())
{
return PartialView("AjaxEdit", new TestData() { Name = name });
}
return View(new TestData() { Name = name });
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(TestData testData)
{
ModelState.AddModelError("name", "incorrect name");
if (!ModelState.IsValid)
{
if (Request.IsAjaxRequest())
{
return PartialView("AjaxEdit");
}
}
return View();
}
}
My Edit View:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<AjaxValidationPartial.Models.TestData>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Edit
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>Edit</h2>
<div id="editForm">
<% Html.RenderPartial("AjaxEdit",Model );%>
</div>
</asp:Content>
My Ajax Partial Edit View
" %>
<%= Html.ValidationSummary("Edit was unsuccessful. Please correct the errors and try again.") %>
<% using (Ajax.BeginForm(new AjaxOptions { UpdateTargetId = "editForm" }))
{%>
<fieldset>
<legend>Fields</legend>
<p>
<label for="Name">Name:</label>
<%= Html.TextBox("Name")%>
<%= Html.ValidationMessage("Name", "*")%>
</p>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
<% } %>
<div>
<%=Html.ActionLink("Back to List", "Index") %>
</div>
And my TestData model class
public class TestData
{
public string Name { get; set; }
}
with this code it works just as you wanted.
notice that i do not pass any model to partial view in my "Post" Edit action.
View that gets rendered gets all the values it needs from a post request.
Alright, I've figured it out. Thanks for the response. For future reference, what I had to do was set up one master divider that all of my content goes into. Then, I always set the updateTargetID to that divider so that it doesn't matter what content it displays, just that it displays it. This actually turns out to be easier because you don't have to waste Javascript functions setting other div tags on and off because you're only using one that is continuously updated.

Resources