How do you deal with Razor Pages PageRemote validation on 'loaded' data (e.g edit ViewModel Page)? - ajax

I am probably missing the obvious, but nevertheless a little stuck with PageRemote validation. Like a lot of us, I am following Mike’s helpful tutorial on the subject: https://www.mikesdotnetting.com/article/343/improved-remote-validation-in-razor-pages
I won’t copy his code here, as it is easy to follow, and works exactly as demonstrated. Great for a ‘Create User’ page!
My problem is though, when applying it to an ‘Edit User’ page, then I have hit a couple snags. In the OnGet() I load the ‘User’ from a QueryString, and populate the form, including the remote validated field. If I touch no fields, and straight away hit the submit button, it doesn’t trigger the submit’s OnPostSubmit() handler, but the PageRemote’s validation OnPost() instead (as presumably the field is dirty, even if the user didn’t do it).
So how do I make sure the submit button fires as expected, in this scenario? According to my break point, it never fires the OnPostSubmit() handler, in this scenario.
Following this scenario, that PageRemote’s OnPost returns ‘true’ (as nothing changed, and everything is still valid), but something else seems to be going on, as a SelectList that is normally loaded OnGet() is now empty, and means the form is now not complete. If before I click the submit button, I enter any of the form’s fields, and force the PageRemote to normally fire, my SelectList is fine. The loss of loaded SelectList values being lost, is only when the PageRemote fires when immediately clicking submit without touching any fields. Why does it behave differently? Surely I am not suppose to be reloading data in this PageRemote validation OnPost() handler, especially in the normal scenario’s, I don’t have to…
I hope this makes sense, and I hope I have not upset anyone by not putting any code up. I am happy to edit my questions with some code, but it is 99% as in Mike’s article. The only difference I have, is populating the ViewModel and SelectList OnGet().
EDIT for code:
#page
#model Redbook.Pages.Test.EditAccountModel
#{
ViewData["Title"] = "EditAccount";
}
<h1>EditAccount</h1>
<form method="post" id="frmUserDetails">
<div class="form-group">
<label class="pt-1">Email</label>
<input id="txtEmail" type="email" inputmode="email" class="form-control" asp-for="Email">
<span class="text-danger" asp-validation-for="Email"></span>
</div>
<div class="form-group">
<label class="pt-1">User Select Option</label>
<select class="form-control" asp-for="UserSelectListOption" asp-items="Model.UserSelectListOptions"></select>
<span class="text-danger" asp-validation-for="UserSelectListOption"></span>
</div>
<button id="btnContinue" type="submit" asp-page-handler="Continue" class="btn btn-outline-info">
Save
</button>
</form>
#section Scripts
{
<script src="~/lib/jquery/dist/jquery.min.js"></script>
#await Html.PartialAsync("_ValidationScriptsPartial")
<script src="~/lib/jquery-ajax-unobtrusive/dist/jquery.unobtrusive-ajax.min.js"></script>
}
CodeBehind
public class EditAccountModel : PageModel
{
[Required(ErrorMessage = "Email Address Required")]
[EmailAddress(ErrorMessage = "Invalid Email Address")]
[PageRemote(
ErrorMessage = "Email/User already in use.",
AdditionalFields = "__RequestVerificationToken",
HttpMethod = "post",
PageHandler = "CheckEmail"
)]
[BindProperty]
public string Email { get; set; }
[Required(ErrorMessage = "User Select List Option Required")]
[BindProperty]
public int UserSelectListOption { get; set; }
public SelectList UserSelectListOptions { get; set; }
public async Task<IActionResult> OnGet()
{
//Normally would pass param (querystring) to load 'user' to edit, but this is just a test!
await Task.CompletedTask;
Email = "joe.bloggs#test.com";
UserSelectListOption = 2;
//Our user form needs a drop down option.
LoadSelectList();
return Page();
}
public async Task<IActionResult> OnPostContinueAsync()
{
if (!ModelState.IsValid)
{
LoadSelectList();
return Page();
}
//Normally we would do something here (e.g get UserID), but again, this is just a test!
await Task.CompletedTask;
//We dont hit this when we first hit submit, unless we pass focus to the 'Email' control first.
//Instead 'OnPostCheckEmail' is triggered only
//Not only that, but when that happens, 'UserSelectListOptions' is empty, so we fail the required validation for that control.
//If we do touch the 'Email' control before submission, validation triggers normally,
//It doesn't affect 'UserSelectListOptions'
//Then we do hit this point successfully.
return Page();
}
public JsonResult OnPostCheckEmail()
{
var existingEmails = new[] { "jane#test.com", "claire#test.com", "dave#test.com" };
var valid = !existingEmails.Contains(Email);
return new JsonResult(valid);
}
public void LoadSelectList()
{
List<SelectListOption> selectListOptions = new List<SelectListOption>();
selectListOptions.Add(new SelectListOption(1, "Option1"));
selectListOptions.Add(new SelectListOption(2, "Option2"));
selectListOptions.Add(new SelectListOption(3, "Option3"));
UserSelectListOptions = new SelectList(selectListOptions, "OptionID", "OptionName");
}
public class SelectListOption
{
public SelectListOption(int optionID, string optionName)
{
this.OptionID = optionID;
this.OptionName = optionName;
}
public int OptionID { get; set; }
public string OptionName { get; set; }
}
}

The "[PageRemote ...]" example didn't work with my bound viewModel; so I created some script to onblur put the email text into my asp-for viewMode.Email input; that way onsubmit, my viewModel.Email has the email value already
enter code here
<input class="viewEmail-input" asp-for="viewModel.Email" />
...
<input asp-for="Email" placeholder="Email" class="email-input" />
$(document).ready(function () {
$('.email-label').on("blur", function () {
var value = $('.email-input').val();
$('.viewEmail-input').val(value);
});
});
I also put my PageRemote into a seperate common cshtml file so more than one razor page can call the same code RemoteValidation_cshtml_cs
Then modified the PageRemote as follows:
enter code here
[PageRemote(
ErrorMessage = "Email Address already exists",
AdditionalFields = "__RequestVerificationToken",
HttpMethod = "post",
PageHandler = "CheckEmail",
PageName = "RemoteValidation"
)]
[Required, EmailAddress]
[RegularExpression(#"^[a-zA-Z0-9_.+-]+#[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$", ErrorMessage = "Invalid Email format")]
[BindProperty]
public string Email { get; set; }
Finally, I notice that there was a database call everytime a character was typed in or deleted from the email input textbox; to prevent this I created a quick routine to verify the passed in email was actually an email address before checking the database; for now, it still checks after typing "c" "o" and "m", but that's only three calls instead of dozens.
enter code here
public async Task<JsonResult> OnPostCheckEmail(string email)
{
// In order to not call the database, check if email is valid email before calling database
// Have to return true... so error is not shown to user; other validation will catch it on submit.
if (!IsValidEmailFormat(email)) return new JsonResult(true);
var user = await _userManager.FindByEmailAsync(email);
if (user == null)
{
return new JsonResult(true);
}
return new JsonResult($"Email { email } is already in use");
}
private bool IsValidEmailFormat(string email)
{
String AllowedChars = #"^[a-zA-Z0-9_.+-]+#[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$";
if (Regex.IsMatch(email, AllowedChars))
{
return true;
}
return false;
}
}

Related

.Net Core Razor Pages - Refresh fields after post -unobtrusive ajax

I have created a .Net Core Razor Pages Application. There are two input fields and a submit button in a razor page. When I click on the button, the numbers in the input fields needs to be incremented. There is a message ‘Hello World’ which is assigned in the OnGet() method.
To keep the message, I used unobtrusive ajax. In this case, the message will remain there but the numbers will not increment. Is there any way to refresh the numbers without writing code in ajax call back method to assign values individually to each element?
Ultimately, my aim is to post a portion of a page and refresh the bind data in the fields on post back without assigning values to the controls individually in ajax call back. Code sample is given below
Note:Need to do this without the whole page relaod.
Index.cshtml
#page
#model IndexModel
#{
ViewData["Title"] = "Home page";
}
<h1>#Model.Message</h1>
<form method="post" data-ajax="true" data-ajax-method="post" >
<div>
<input type="text" asp-for="Num1" />
<input type="text" asp-for="Num2" />
<input type="submit" value="Submit" />
</div>
</form>
Index.cshtml.cs
public class IndexModel : PageModel
{
[BindProperty]
public int Num1 { get; set; } = 0;
[BindProperty]
public int Num2 { get; set; } = 0;
public string Message { get; set; }
public void OnGet()
{
Message = "Hello World";
GetNumbers();
}
void GetNumbers()
{
Num1 += 1;
Num2 += 5;
}
public IActionResult OnPost()
{
GetNumbers();
return Page();
}
}
ModelState.Remove("Nmu1");
ModelState.Remove("Nmu2");
Similarly to ASP.NET WebForms, ASP.NET Core form state is stored in ModelState. After posting, the form will be loaded with the the binding values, then updated with ModelState. So there is a need to clear the values within ModelState, otherwise the values will be overwritten.

MVC3 Custom Validation error message doesn't display when using ViewModel

SUMMARY
Question: Why doesn't the custom validation error message show when using a ViewModel.
Answer: The custom validation should be applied to the ViewModel not the Class. See the end of #JaySilk84's answer for example code.
MVC3, project using
jquery-1.7.2.min.js
modernizr-2.5.3.js
jquery-ui-1.8.22.custom.min.js (generated by jQuery.com for the Accordion plugin)
jquery.validate.min.js and
jquery.validate.unobtrusive.min.js
I have validation working in my project for both dataannotations in the View and for ModelState.AddModelError in the Controller so I know I have all the validation code configured properly.
But with custom validation an error is generated in the code but the error message doesn't display.
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{ if (DOB > DateTime.Now.AddYears(-18))
{ yield return new ValidationResult("Must be 18 or over."); } }
Drilling down in debug in the POST action the custom validation causes Model state to fail and the error message is placed in the proper value field but when the model is sent back to the view the error message doesn't display. In the controller I also have ModelState.AddModelError code and its message does display. How is that handled differently as to one would work and not the other? If not that what else would prevent the error message from displaying?
Update 1 :
I'm using a ViewModel to create the model in the view. I stripped out the ViewModel and the error message started displaying, as soon I added the ViewModel back in the message again stopped displaying. Has anyone successfully used a custom validation with a ViewModel? Was there anything you had to do extra to get it to work?
Update 2 :
I created a new MVC3 project with these two simple classes (Agency and Person).
public class Agency : IValidatableObject
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime DOB { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (DOB > DateTime.Now.AddYears(-18)) { yield return new ValidationResult("Must be over 18."); }
}
}
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
Here's the Controller Code
public ActionResult Create()
{
return View();
}
//
// POST: /Agency/Create
[HttpPost]
public ActionResult Create(Agency agency)
{
if (ModelState.IsValid)
{
db.Agencies.Add(agency);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(agency);
}
//[HttpPost]
//public ActionResult Create(AgencyVM agencyVM)
//{
// if (ModelState.IsValid)
// {
// var agency = agencyVM.Agency;
// db.Agencies.Add(agency);
// db.SaveChanges();
// return RedirectToAction("Index");
// }
// return View(agencyVM);
//}
The View
#model CustValTest.Models.Agency
#*#model CustValTest.Models.AgencyVM*#
#* When using VM (model => model.Name) becomes (model => model.Agency.Name) etc. *#
#{
ViewBag.Title = "Create";
}
<h2>Create</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>Agency</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.DOB)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.DOB)
#Html.ValidationMessageFor(model => model.DOB)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
The ViewModel
public class AgencyVM
{
public Agency Agency { get; set; }
public Person Person { get; set; }
}
When just Agency is presented in the View the validation error displays (DOB under 18). When the ViewModel is presented the error doesn't display. The custom validation always catches the error though and causes ModelState.IsValid to fail and the view to be re-presented. Can anyone replicate this? Any ideas on why and how to fix?
Update 3 :
As a temporary work around I have changed the Validation into a field level one (vs. a model level one) by adding a parameter to the ValidationResult:
if (DOB > DateTime.Now.AddYears(-18)) { yield return new ValidationResult("Must be over 18.", new [] { "DOB" }); }
The problem with this is now the error message is showing up next to the field rather than at the top of the form (which is not good in say an accordion view since the user will be returned to the form with no visible error message). To fix this secondary problem I added this code to the Controller POST action.
ModelState.AddModelError(string.Empty, errMsgInvld);
return View(agencyVM);
}
string errMsgInvld = "There was an entry error, please review the entire form. Invalid entries will be noted in red.";
The question is still unanswered, why doesn't the model level error message show with a ViewModel (see my response to JaySilk84 for more on this)?
The issue is now that your models are nested, the error message is being placed into ModelState under Agency without the .DOB because you didn't specify it in the ValidationResult. The ValidationMessageFor() helper is looking for a key named Agency.DOB (see relevant code below from ValidationMessageFor() helper):
string fullHtmlFieldName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(expression);
FormContext clientValidation = htmlHelper.ViewContext.GetFormContextForClientValidation();
if (!htmlHelper.ViewData.ModelState.ContainsKey(fullHtmlFieldName) && clientValidation == null)
return (MvcHtmlString) null;
GetFullHtmlFieldName() is returning Agency.DOB, not Agency
I think if you add the DOB to the ValidationResult it will work:
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (DOB > DateTime.Now.AddYears(-18)) { yield return new ValidationResult("Must be over 18.", new List<string>() { "DOB" }); }
}
That second parameter to ValidationResult will tell it what key to use in ModelState (By default it will append the parent object which is Agency) so the ModelState will have a key named Agency.DOB which is what your ValidationMessageFor() is looking for.
Edit:
If you don't want field level validation then you don't need the Html.ValidationMessageFor(). You just need the ValidationSummary().
The view is treating AgencyVM as the model. If you want it to validate properly then put the validation at the AgencyVM level and have it validate the child objects. Alternatively you could put validation on the child objects but the parent object (AgencyVM) has to aggregate it to the view. Another thing you can do is keep it as it is and change ValidationSummary(true) to ValidationSummary(false). This will print everything in ModelState to the summary. I think removing the validation from Agency and putting it on AgencyVM might be the best approach:
public class AgencyVM : IValidatableObject
{
public Agency Agency { get; set; }
public Person Person { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (Agency.DOB > DateTime.Now.AddYears(-18)) { yield return new ValidationResult("Must be over 18."); }
if (string.IsNullOrEmpty(Agency.Name)) { yield return new ValidationResult("Need a name"); }
}
}

MVC3: button to send both form (model) values and an extra parameter

In an MVC3 project, i use an Html.BeginForm to post some (model-)values. Along with those i want to send an extra parameter that is not part of the form (the model) but in the ViewBag. Now, when i use a Button (code in answer here: MVC3 razor Error in creating HtmlButtonExtension), all the form values are posted but the extra parameter remains null. When i use an ActionLink, the parameter is posted but the form values are not :) Any know how i can combine the two? Thanks!
#Html.Button("Generate!", new { id = ViewBag.ProjectID })
#Html.ActionLink("Generate!", "Post", new { id = #ViewBag.ProjectID })
My advice would be to declare a new Object in your App.Domain.Model something like this
namespace App.Domain.Model
{
public class CustomEntity
{
public Project projectEntity { get; set; }
public int variableUsed { get; set; }
}
}
In your view you can acces them easily by using CustomEntity.projectEntity and CustomEntity.variableUsed.
Hope it helps
You can do something like below.
View code
#using (Html.BeginForm("ActionName", "ControllerName", FormMethod.Post, new { #id = "frmId", #name = "frmId" }))
{
#*You have to define input as a type button not as a sumit. you also need to define hidden variable for the extra value.*#
<input type="hidden" name="hndExtraParameter" id="hndExtraParameter" />
<input value="Submit" type="button" id="btnSubmit" onclick="UpdateHiddenValue()" />
}
<script type="text/javascript">
function ValidateUser() {
$("#hndExtraParameter").val('Assignvaluehere');
$("#frmId").submit();
}
</script>
Controller Code
[HttpPost]
public ActionResult ActionName(Model model, string hndExtraParameter)
{
//Do your operation here.
}

ASP.NET MVC 3 CheckboxFor retains previous value, despite Model value

I'm attempting to add a classic Accept Terms and Conditions checkbox on the log on page of an MVC application.
If the user accepts the Terms and Conditions, but fails to log on for some other reason (bad password etc), then I want the Accept T&Cs checkbox not to be checked, so the user is forced to accept the T&Cs on every log on attempt.
The problem is that using Html.CheckboxFor(), after a postback the checkbox retains its previous value, despite the value of the bound Model property.
Here's the code, stripped down to essentials. If you run this code up, check the checkbox, and click the button, you'll be returned to the form with the checkbox still checked, even though the bound model property is false.
The Model:
namespace Namespace.Web.ViewModels.Account
{
public class LogOnInputViewModel
{
[IsTrue("You must agree to the Terms and Conditions.")]
public bool AcceptTermsAndConditions { get; set; }
}
}
The validation attribute:
public class IsTrueAttribute : ValidationAttribute
{
public IsTrueAttribute(string errorMessage) : base(errorMessage)
{
}
public override bool IsValid(object value)
{
if (value == null) return false;
if (value.GetType() != typeof(bool)) throw new InvalidOperationException("can only be used on boolean properties.");
return (bool)value;
}
}
The View:
#model Namespace.Web.ViewModels.Account.LogOnInputViewModel
#using (Html.BeginForm()) {
#Html.CheckBoxFor(x => x.AcceptTermsAndConditions)
<input type="submit" value="Log On" />
}
The Controller:
[HttpGet]
public ActionResult LogOn(string returnUrl)
{
return View(new LogOnInputViewModel { AcceptTermsAndConditions = false });
}
[HttpPost]
public ActionResult LogOn(LogOnInputViewModel input)
{
return View(new LogOnInputViewModel { AcceptTermsAndConditions = false });
}
I saw the suggestion on asp.net to add a #checked attribute to the CheckboxFor. I tried this, making the view
#model Namespace.Web.ViewModels.Account.LogOnInputViewModel
#using (Html.BeginForm()) {
#Html.CheckBoxFor(x => x.AcceptTermsAndConditions, new { #checked = Model.AcceptTermsAndConditions })
<input type="submit" value="Log On" />
}
And I saw the same behaviour.
Thanks for any help/insights!
Edit: Although I want to override the posted back value, I wish to retain the message if validation of AcceptTermsAndConditions fails (there is a validation attribute on AcceptTermsAndConditions requiring it to be true), so I can't use ModelState.Remove("AcceptTermsAndConditions") which was the otherwise sound answer #counsellorben gave me. I've edited the code above to include the validation attribute - apologies to #counsellorben for not being clearer originally.
You need to clear the ModelState for AcceptTermsAndConditions. By design, CheckBoxFor and other data-bound helpers are bound first against the ModelState, and then against the model if there is no ModelState for the element. Add the following to your POST action:
ModelState.Remove("AcceptTermsAndConditions");

MVC3 RemoteAttribute and muliple submit buttons

I have discovered what appears to be a bug using MVC 3 with the RemoteAttibute and the ActionNameSelectorAttribute.
I have implemented a solution to support multiple submit buttons on the same view similar to this post: http://blog.ashmind.com/2010/03/15/multiple-submit-buttons-with-asp-net-mvc-final-solution/
The solution works however, when I introduce the RemoteAttribute in my model, the controllerContext.RequestContext.HttpContext.Request no longer contains any of my submit buttons which causes the the "multi-submit-button" solution to fail.
Has anyone else experienced this scenario?
I know this is not a direct answer to your question, but I would propose an alternative solution to the multiple submit-buttons using clientside JQuery and markup instead:
Javascript
<script type="text/javascript">
$(document).ready(function () {
$("input[type=submit][data-action]").click(function (e) {
var $this = $(this);
var form = $this.parents("form");
var action = $this.attr('data-action');
var controller = $this.attr('data-controller');
form.attr('action', "/" + controller + "/" + action);
form.submit();
e.preventDefault();
});
});
</script>
Html
#using (Html.BeginForm())
{
<input type="text" name="name" id="name" />
<input type="submit" value="Save draft" data-action="SaveDraft" data-controller="Home" />
<input type="submit" value="Publish" data-action="Publish" data-controller="Home" />
}
It might not be as elegant as a code-solution, but it offers somewhat less hassle in that the only thing that actually changes is the action-attribute of the form when a submitbutton is clicked.
Basically what it does is that whenever a submit-button with the attribute data-action set is clicked, it replaces its parent forms action-attribute with a combination of the attributes data-controller and data-action on the clicked button, and then fires the submit-event of the form.
Of course, this particular example is poorly generic and it will always create /Controller/Action url, but this could easily be extended with some more logic in the click-action.
Just a tip :)
i'm not sure that its a bug in mvc 3 as it's not something that you were expecting. the RemoteAttribute causes javascript to intercept and validate the form with an ajax post. to do that, the form post is probably canceled, and when the validation is complete, the form's submit event is probably called directly, rather than using the actual button clicked. i can see where that would be problematic in your scenario, but it makes sense. my suggestion, either don't use the RemoteAttributeand validate things yourself, or don't have multiple form actions.
The problem manifests itself when the RemoteAttribute is used on a model in a view where mutliple submit buttons are used. Regardless of what "multi-button" solution you use, the POST no longer contains any submit inputs.
I managed to solve the problem with a few tweeks to the ActionMethodSelectorAttribute and the addition of a hidden view field and some javascript to help wire up the pieces.
ViewModel
public class NomineeViewModel
{
[Remote("UserAlreadyRegistered", "Nominee", AdditionalFields="Version", ErrorMessage="This Username is already registered with the agency.")]
public string UserName { get; set; }
public int Version {get; set;}
public string SubmitButtonName{ get; set; }
}
ActionMethodSelectorAttribute
public class OnlyIfPostedFromButtonAttribute : ActionMethodSelectorAttribute
{
public String SubmitButton { get; set; }
public String ViewModelSubmitButton { get; set; }
public override Boolean IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
{
var buttonName = controllerContext.HttpContext.Request[SubmitButton];
if (buttonName == null)
{
//This is neccessary to support the RemoteAttribute that appears to intercepted the form post
//and removes the submit button from the Request (normally detected in the code above)
var viewModelSubmitButton = controllerContext.HttpContext.Request[ViewModelSubmitButton];
if ((viewModelSubmitButton == null) || (viewModelSubmitButton != SubmitButton))
return false;
}
// Modify the requested action to the name of the method the attribute is attached to
controllerContext.RouteData.Values["action"] = methodInfo.Name;
return true;
}
}
View
<script type="text/javascript" language="javascript">
$(function () {
$("input[type=submit][data-action]").click(function (e) {
var action = $(this).attr('data-action');
$("#SubmitButtonName").val(action);
});
});
</script>
<% using (Html.BeginForm())
{%>
<p>
<%= Html.LabelFor(m => m.UserName)%>
<%= Html.DisplayFor(m => m.UserName)%>
</p>
<input type="submit" name="editNominee" value="Edit" data-action="editNominee" />
<input type="submit" name="sendActivationEmail" value="SendActivationEmail" data-action="sendActivationEmail" />
<%=Html.HiddenFor(m=>m.SubmitButtonName) %>
<% } %>
Controller
[AcceptVerbs(HttpVerbs.Post)]
[ActionName("Details")]
[OnlyIfPostedFromButton(SubmitButton = "editNominee", ViewModelSubmitButton = "SubmitButtonName")]
public ActionResult DetailsEditNominee(NomineeViewModel nom)
{
return RedirectToAction("Edit", "Nominee", new { id = nom.UserName });
}
[AcceptVerbs(HttpVerbs.Post)]
[ActionName("Details")]
[OnlyIfPostedFromButton(SubmitButton = "sendActivationEmail", ViewModelSubmitButton = "SubmitButtonName")]
public ActionResult DetailsSendActivationEmail(NomineeViewModel nom)
{
return RedirectToAction("SendActivationEmail", "Nominee", new { id = nom.UserName });
}
[OutputCache(Location = OutputCacheLocation.None, NoStore = true)]
public ActionResult UserAlreadyRegistered(string UserName, int Version)
{
//Only validate this property for new records (i.e. Version != zero)
return Version != 0 ? Json(true, JsonRequestBehavior.AllowGet)
: Json(! nomineeService.UserNameAlreadyRegistered(CurrentLogonDetails.TaxAgentId, UserName), JsonRequestBehavior.AllowGet);
}
I encountered the same issue.
I also attached an on submit event to prepare the form before submit. Interestingly, when I insert a break point in the on submit function, and then continue, the problem has disappeared.
I ended up with an Ajax form by removing the Remote attribute and validate the field using the ModelState.

Resources