asp.net mvc 2 client side validation missing ValidationRules on custom attribute - validation

Can't seem to get checkbox to be validate on client-side using asp.net mvc 2. Here is my code.
Model
[Serializable]
public class RegistrationModel
{
bool termsAndCondition = false;
[RequiredToBeTrue(ErrorMessage = "Need terms and service")]
public bool TermsAndConditions
{
get
{
return termsAndCondition;
}
set
{
termsAndCondition = value;
}
}
}
Custom Attribute
public class RequiredToBeTrueAttribute : RequiredAttribute
{
public override bool IsValid(object value)
{
return (value != null) && (value is bool) ? (bool)value : false;
}
}
View
<%# Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage<RegistrationModel>" %>
<% Html.EnableClientValidation(); %>
<% using (Html.BeginForm("Register", "Registration", new { area="Account", id = "openid_form", inRegistration = true }))
<%=Html.ValidationSummary(false) %>
blah blah blah
<div class="checkbox"><label><%= Html.CheckBoxFor(model => model.TermsAndConditions) %>I agree to the terms and conditions of use.</label></div>
<input type="submit" id="submit" name="submit" value="Join Now" />
<%
Html.ValidateFor(m => m.TermsAndConditions);
%>
<% } %>
I am trying to call Html.ValidateFor at the end to push up all error message at top of the page. However, the property "TermsAndConditions" is not getting validated on client side (works great on server side). This leads me to look at the the window.mvcClientValidationMetData method at that mvc push out and I saw the following:
{"FieldName":"TermsAndConditions","ReplaceValidationMessageContents":false,"ValidationMessageId":null,"ValidationRules":[]}
Which you can see that "ValidationRules" are empty meaning that it is trying to validate it but the error message wasn't push out to the client for some reason.
Any ideas? Any help is appreciated.

Seems like I need to do more digging first. Was hoping the new attribute will appear magically on the client side. Instead, have to write some customer javascript to wire it up. See phil hack's post for detail.

This article from Phil Haack, ASP.NET MVC 2 Custom Validation, should help point you in the right direction.
Basically you need to create your own DataAnnotationsModelValidator<RequiredToBeTrueAttribute> and then write some client side script to get it done.
HTHs,
Charles

Related

Handling multiple submit button in form

i was looking for good trick to handle multiple submit button in form and then i got some advice from this url and i followed but fail.
How do you handle multiple submit buttons in ASP.NET MVC Framework?
posted by #Andrey Shchekin.
he just said create a class like below one so i did in same controller
public class HttpParamActionAttribute : ActionNameSelectorAttribute {
public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo) {
if (actionName.Equals(methodInfo.Name, StringComparison.InvariantCultureIgnoreCase))
return true;
if (!actionName.Equals("Action", StringComparison.InvariantCultureIgnoreCase))
return false;
var request = controllerContext.RequestContext.HttpContext.Request;
return request[methodInfo.Name] != null;
}
}
then multiple submit button in the view look like & also controller code look like below
<% using (Html.BeginForm("Action", "Post")) { %>
<!— …form fields… -->
<input type="submit" name="saveDraft" value="Save Draft" />
<input type="submit" name="publish" value="Publish" />
<% } %>
and controller with two methods
public class PostController : Controller {
[HttpParamAction]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult SaveDraft(…) {
//…
}
[HttpParamAction]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Publish(…) {
//…
}
}
but when i test his code it never work. so any can tell me where i am making the mistake or code itself is wrong for handling the situation. thanks
View:
<input type="submit" name="mySubmit" value="Save Draft" />
<input type="submit" name="mySubmit" value="Publish" />
Controller Action:
[HttpPost]
public ActionResult ActionName(ModelType model, string mySubmit)
{
if(mySubmit == "Save Draft")
{
//save draft code here
} else if(mySubmit == "Publish")
{
//publish code here
}
}
I had to deal with the similar scenario when I had the requirement that Users can finalize or save progress of the hospital infant record - essentially both actions are submit but one validates the record for insertion into the main DB table and another one saves it into a temp table without any validation. I handled it like this:
I have 2 buttons both are type submit with different IDs (btnSave and btnFinalize). When btnSave is clicked I intercept that event with some JQuery code:
$("#btnSave").click(function () {
$("#SaveForm").validate().settings.rules = null;
$('#SaveForm').attr('action', '#(Url.Content("~/Home/EditCase?finalize=false"))');
});
As you can see I modify the action attribute of the form to point to a different URL with a querystring attribute of finalize = false. I also remove any validation present on the model. If the other button is clicked I do nothing - executes the default behavior.
And in my controller I have a single action that handles both submit actions:
public ActionResult EditCase(EditInfantModel model, bool finalize = true)
{
// Logic for handling submit in here...
}
I think you can apply the similar technique for your problem. I'm not sure if it's the answer you're looking for but I thought it was worth mentioning...

How do I convert this to MVC 3?

I have this on a base page class in asp.net 4.
How do I convert or do this on mvc 3?
protected void ForceFullRefreshInFF()
{
if (Request.Browser.Browser == "Firefox")
Form.Attributes.Add("autocomplete", "off");//This fails
}
I think that you may want to store that in your ViewData. This allows you to use this value across all pages and partials. I believe "Form" in your case will be targeting the actual FormCollection.
protected void ForceFullRefreshInFF()
{
if (Request.Browser.Browser == "Firefox")
ViewData["autocomplete"] = "off";//This fails
}
Then in your view you can do:
<form autocomplete="<%: ViewData["autocomplete"] %>" ...>
...
</form>

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.

Razor syntax to declare action that renders html

I'm trying to create an action that renders some html in the razor view engine. This was pretty easy in the webforms engine but I'm having some issues with razor. Here is the simplest form of what I'm trying to accomplish using the webforms engine:
<% var myAction = new Action<HtmlHelper<int>>((helper) => { %>
<div>
<%= helper.ViewData.Model %>
</div>
<%}); %>
The closest I've come in the razor view engine is:
#{var myAction = new Action<HtmlHelper<int>>((help) =>
{
#<div>
#help.ViewData.Model
</div>;
});
}
This gives a "CS0201: Only assignment, call, increment, decrement, and new object expressions can be used as a statement" error.
Any help would be appreciated. Thanks.
#{
Func<dynamic, object> myAction =
#<div>
#item.ProductName
</div>;
}
#myAction(Model)
You may also checkout the following blog post.
UPDATE:
You may also do this:
#{
Func<HtmlHelper<int>, object> myAction = #<div>#item.ViewData.Model</div>;
}
or:
#{
Func<dynamic, object> myAction = #<div>#item.ViewData.Model</div>;
}
and to invoke:
#myAction(someInstanceOfTheRequiredHelper)

Asp.Net MVC2 Clientside Validation problem with controls with prefixes

The problem is: when I put 2 controls of the same type on a page I need to specify different prefixes for binding. In this case the validation rules generated right after the form are incorrect. So how to get client validation work for the case?:
the page contains:
<%
Html.RenderPartial(ViewLocations.Shared.PhoneEditPartial, new PhoneViewModel { Phone = person.PhonePhone, Prefix = "PhonePhone" });
Html.RenderPartial(ViewLocations.Shared.PhoneEditPartial, new PhoneViewModel { Phone = person.FaxPhone, Prefix = "FaxPhone" });
%>
the control ViewUserControl<PhoneViewModel>:
<%= Html.TextBox(Model.GetPrefixed("CountryCode"), Model.Phone.CountryCode) %>
<%= Html.ValidationMessage("Phone.CountryCode", new { id = Model.GetPrefixed("CountryCode"), name = Model.GetPrefixed("CountryCode") })%>
where Model.GetPrefixed("CountryCode") just returns "FaxPhone.CountryCode" or "PhonePhone.CountryCode" depending on prefix
And here is the validation rules generated after the form. They are duplicated for the field name "Phone.CountryCode". While the desired result is 2 rules (required, number) for each of the FieldNames "FaxPhone.CountryCode", "PhonePhone.CountryCode"
alt text http://www.freeimagehosting.net/uploads/37fbe720bf.png
The question is somewhat duplicate of Asp.Net MVC2 Clientside Validation and duplicate ID's problem
but the advise to manually generate ids doesn't helps.
Correct way to set the same prefixes both for textbox and validation:
<% using (Html.BeginHtmlFieldPrefixScope(Model.Prefix)) { %>
<%= Html.TextBoxFor(m => m.Address.PostCode) %>
<%= Html.ValidationMessageFor(m => m.Address.PostCode) %>
<% } %>
where
public static class HtmlPrefixScopeExtensions
{
public static IDisposable BeginHtmlFieldPrefixScope(this HtmlHelper html, string htmlFieldPrefix)
{
return new HtmlFieldPrefixScope(html.ViewData.TemplateInfo, htmlFieldPrefix);
}
private class HtmlFieldPrefixScope : IDisposable
{
private readonly TemplateInfo templateInfo;
private readonly string previousHtmlFieldPrefix;
public HtmlFieldPrefixScope(TemplateInfo templateInfo, string htmlFieldPrefix)
{
this.templateInfo = templateInfo;
previousHtmlFieldPrefix = templateInfo.HtmlFieldPrefix;
templateInfo.HtmlFieldPrefix = htmlFieldPrefix;
}
public void Dispose()
{
templateInfo.HtmlFieldPrefix = previousHtmlFieldPrefix;
}
}
}
(by chance found the solution in the code on Steve Sanderson's blog http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/)
Also looks like Html.EditorFor approach should work as well as suggested here: ASP.NET MVC 2 - ViewModel Prefix

Resources