I want to use unobtrusive validation on my MVC app.
I've created class
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = true)]
public sealed class RequiredIfAttribute : ValidationAttribute, IClientValidatable
{
private const string _defaultErrorMessage = "{0} is required";
private string _targetPropertyName;
private bool _targetPropertyCondition;
public RequiredIfAttribute(string targetPropertyName, bool targetPropertyCondition)
: base(_defaultErrorMessage)
{
this._targetPropertyName = targetPropertyName;
this._targetPropertyCondition = targetPropertyCondition;
}
public override string FormatErrorMessage(string name)
{
return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString, name, _targetPropertyName, _targetPropertyCondition);
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
bool result = false;
bool propertyRequired = false;
var targetProperty = validationContext.ObjectType.GetProperty(_targetPropertyName);
var targetPropertyValue = (bool)targetProperty.GetValue(validationContext.ObjectInstance, null);
if (targetPropertyValue == _targetPropertyCondition)
{
propertyRequired = true;
}
if (propertyRequired)
{
if (value == null)
{
var message = FormatErrorMessage(validationContext.DisplayName);
return new ValidationResult(message);
}
}
return null;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule();
rule.ErrorMessage = FormatErrorMessage(metadata.GetDisplayName());
rule.ValidationType = "requiredif";
rule.ValidationParameters.Add("targetpropertyname", this._targetPropertyName);
rule.ValidationParameters.Add("targetpropertyvalue", this._targetPropertyCondition.ToString().ToLower());
yield return rule;
}
}
I have validation function on client
$(function () {
$.validator.addMethod("requiredif", function (value, element, param) {
if ($(param.propertyname).is(':checked').toString() == param.propertyvalue) {
if (!this.depend(param, element))
return "dependency-mismatch";
switch (element.nodeName.toLowerCase()) {
case 'select':
var val = $(element).val();
return val && val.length > 0;
case 'input':
if (this.checkable(element))
return this.getLength(value, element) > 0;
default:
return $.trim(value).length > 0;
}
}
return true;
});
$.validator.unobtrusive.adapters.add("requiredif", ["targetpropertyname", "targetpropertyvalue"], function (options) {
if (options.element.tagName.toUpperCase() !== "INPUT" || options.element.type.toUpperCase() !== "CHECKBOX") {
options.rules["requiredif"] = {
propertyname: "#" + options.params.targetpropertyname,
propertyvalue: options.params.targetpropertyvalue
};
options.messages["requiredif"] = options.message;
}
});
} (jQuery));
But that does not validate. Why?
Check your web.config if you have there smth. like this
<appSettings>
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
of course you should use proper attributes in your model
Use valid js file.
https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.11/jquery.validate.unobtrusive.min.js
Can also be through the controller:
HtmlHelper.ClientValidationEnabled = true;
HtmlHelper.UnobtrusiveJavaScriptEnabled = true;
Related
I have implemented Complex custom Foolproof validation in my application from this link but sadly its not working.My requirement is simple,I have a input file for uploading an image and there should be a validation if the user chooses to upload file other than specified below
".jpg",".png",".gif",".jpeg"
Code is
[Required(ErrorMessage = "Please upload Photo", AllowEmptyStrings = false)]
[IsValidPhoto(ErrorMessage="Please select files of type .jpg,.png,.gif,.jpeg")]
public HttpPostedFileBase PhotoUrl { get; set; }
public class IsValidPhotoAttribute : ModelAwareValidationAttribute
{
//this is needed to register this attribute with foolproof's validator adapter
static IsValidPhotoAttribute() { Register.Attribute(typeof(IsValidPhotoAttribute)); }
public override bool IsValid(object value, object container)
{
if (value != null)
{
string[] AllowedFileExtensions = new string[] { ".jpg", ".gif", ".png", ".jpeg" };
var file = value as HttpPostedFileBase;
if (!AllowedFileExtensions.Contains(file.FileName.Substring(file.FileName.LastIndexOf('.'))))
{
return false;
}
}
return true;
}
}
CSHTML is
#Html.TextBoxFor(m => m.PhotoUrl, new { #class = "form-control imgUpload",
#placeholder = "Please upload Photo", #id = "txtPhoto", #type = "file" })
#Html.ValidationMessageFor(m => m.PhotoUrl)
You will not be able to get client side validation unless you also create a script to add the rules. It is not necessary to use foolproof and the following method and scripts will give you both server and client side validation
public class FileAttachmentAttribute : ValidationAttribute, IClientValidatable
{
private List<string> _Extensions { get; set; }
private const string _DefaultErrorMessage = "Only file types with the following extensions are allowed: {0}";
public FileAttachmentAttribute(string fileExtensions)
{
_Extensions = fileExtensions.Split('|').ToList();
ErrorMessage = _DefaultErrorMessage;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
HttpPostedFileBase file = value as HttpPostedFileBase;
if (file != null)
{
var isValid = _Extensions.Any(e => file.FileName.EndsWith(e));
if (!isValid)
{
return new ValidationResult(string.Format(ErrorMessageString, string.Join(", ", _Extensions)));
}
}
return ValidationResult.Success;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ValidationType = "fileattachment",
ErrorMessage = string.Format(ErrorMessageString, string.Join(", ", _Extensions))
};
rule.ValidationParameters.Add("extensions", string.Join(",", _Extensions));
yield return rule;
}
}
Scripts
$.validator.unobtrusive.adapters.add('fileattachment', ['extensions'], function (options) {
var params = { fileattachment: options.params.extensions.split(',') };
options.rules['fileattachment'] = params;
if (options.message) {
options.messages['fileattachment'] = options.message;
}
});
$.validator.addMethod("fileattachment", function (value, element, param) {
var extension = getExtension(value);
return $.inArray(extension, param.fileextensions) !== -1;
});
function getExtension(fileName) {
var extension = (/[.]/.exec(fileName)) ? /[^.]+$/.exec(fileName) : undefined;
if (extension != undefined) {
return extension[0];
}
return extension;
};
and then use it as
[FileAttachment("jpg|gif|png|jpeg")]
public HttpPostedFileBase PhotoUrl { get; set; }
I am passing view model data for saving from js to controller like
$(function () {
$.ajax({
url: "/api/BranchSurvey/GetBranchLogicConditions?qId=" + questionId + "&isAdd=false",
success: function (branchLogicdata) {
BrSViewModel = ko.mapping.fromJS(branchLogicdata);
$.ajax({
url: "/api/BranchSurvey/BranchingLogicQuestion?questionId=" + questionId,
success: function (branchLogicQuestion) {
BrSViewModel.Question = branchLogicQuestion;
ko.applyBindings(BrSViewModel, document.getElementById('mainDiv'));
}
});
}
});
$("#btnSaveLogic").click(function () {
debugger;
$.ajax({
url: "/api/BranchSurvey/SaveBranchLogicConditions",
type: 'post',
data: ko.toJSON(BrSViewModel),
success: function (data) {
branchingSurveyPopupBody.empty();
branchingSurveyPopup.dialog('destroy');
}
});
return false;
});
});
In controller
public class BranchSurveyController : ApiController
{
[HttpPost]
public void SaveBranchLogicConditions(BranchLogic objBranchSurvey)
{
string str = "<condition>";
string qstText = "";
int count = 0;
foreach (BranchCondiotionXml oxml in objBranchSurvey.ListBranchXML)
{
..... (something)
}
}
}
When I debug script in browser, I'm getting values in viewmodel BrSViewModel. But when it goes to controller all data is getting empty.
My entity is
public class BranchLogic
{
#region private members
List<BranchCondiotionXml> lstBranchXML = new List<BranchCondiotionXml>();
Question _question = new Question();
string targetQuestionCondition = string.Empty;
string thenTargetQuestion = string.Empty;
string sucessQuestion = string.Empty;
string thenTargetUrl = string.Empty;
string elseTargetQuestionCondition = string.Empty;
string elseTargetQuestion = string.Empty;
string elseSuccessQuestion = string.Empty;
string elseTargetUrl = string.Empty;
#endregion
#region public members
public List<BranchCondiotionXml> ListBranchXML
{
get { return lstBranchXML; }
set { lstBranchXML = value; }
}
public Question Question
{
get { return _question; }
set { _question = value; }
}
public string TargetQuestionCondition
{
get { return targetQuestionCondition; }
set { targetQuestionCondition = value; }
}
public string ThenTargetQuestion
{
get { return thenTargetQuestion; }
set { thenTargetQuestion = value; }
}
public string SucessQuestion
{
get { return sucessQuestion; }
set { sucessQuestion = value; }
}
public string ThenTargetUrl
{
get { return thenTargetUrl; }
set { thenTargetUrl = value; }
}
public string ElseTargetQuestionCondition
{
get { return elseTargetQuestionCondition; }
set { elseTargetQuestionCondition = value; }
}
public string ElseTargetQuestion
{
get { return elseTargetQuestion; }
set { elseTargetQuestion = value; }
}
public string ElseSuccessQuestion
{
get { return elseSuccessQuestion; }
set { elseSuccessQuestion = value; }
}
public string ElseTargetUrl
{
get { return elseTargetUrl; }
set { elseTargetUrl = value; }
}
#endregion
}
Can u please please tell me where i'm doing wrong. Thanks in advance.
I am using Hidden fields to maintain the state of my checkboxes between page requests except it doesn't work. So when I click on my webgrid to go to the next 15 records it forgets which checkboxes are checked.
Index view
#model IEnumerable<UserManager.Models.vw_UserManager_Model>
#{
ViewBag.Title = "User Manager Dashboard";
}
#Html.ActionLink("Create New User", "CreateUser")
#using (#Html.BeginForm())
{
<div class="webgrid-filter">
#{Html.RenderAction("_WebGridFilter", "UserManager");}
</div>
<div id="webgrid-wrapper">
#Html.Partial("~/Views/Partial/_WebGridUserManager.cshtml", Model)
</div>
<br />
}
<script type="text/javascript">
$(document).ready(function () {
// Disable checkboxs where a user is not active.
$(".webgrid-wrapper input:not(:checked)").attr("disabled", "disabled");
// Style tables.
function jQueryUIStyling() {
$('input:button, input:submit').button();
$('.webgrid-wrapper').addClass('ui-grid ui-widget ui-widget-content ui-corner-all');
$('.webgrid-title').addClass('ui-grid-header ui-widget-header ui-corner-top');
jQueryTableStyling();
} // end of jQueryUIStyling
function jQueryTableStyling() {
$('.webgrid').addClass('ui-grid-content ui-widget-content');
$('.webgrid-header').addClass('ui-state-default');
$('.webgrid-footer').addClass('ui-grid-footer ui-widget-header ui-corner-bottom ui-helper-clearfix');
} // end of jQueryTableStyling
});
</script>
<script type="text/javascript">
function filterGrid() {
var filters = getFilterVals();
$.ajax({
url: '#Url.Action("Index", "UserManager")',
type: "POST",
async: true,
dataType: "html",
data: "alfConnect=" + filters.alfConnect + "&" + "alfIntelligence=" + filters.alfIntelligence + "&" + "brad=" + filters.brad,
success: function (data) {
$('#webgrid-wrapper').empty().html(data);
// $('#webgrid-wrapper').html(data);
}
});
}
function getFilterVals() {
filters = new Object();
if ($('.webgrid-filter #chkFilterAlfIntell').is(':checked')) {
filters.alfIntelligence = 1;
}
else {
filters.alfIntelligence = 0;
}
if ($('.webgrid-filter #chkFilterAlfConn').is(':checked')) {
filters.alfConnect = 1;
}
else {
filters.alfConnect = 0;
}
if ($('.webgrid-filter #chkFilterBrad').is(':checked')) {
filters.brad = 1;
}
else {
filters.brad = 0;
}
return filters;
}
function logUserOff(url) {
var answer = confirm('Are you sure you want to save this data?')
if (answer) {
// alert(url + ": " + value);
$.ajax({
url: url,
type: "POST"
// data: value
}).done(function () {
$(this).addClass("done");
});
return true;
}
else {
return false;
}
};
</script>
Partial view with hidden fields
#model UserManager.Models.webgrid_validation
<b>#Html.Label("Select a filter: ")</b>
<br />
#Html.Label("Toggle ALF Intelligence users:")
#Html.CheckBoxFor(model => model.chkBoxAlfIntelligence, new
{
id = "chkFilterAlfIntell",
onclick = "filterGrid()",
#checked = "checked"
})
#Html.Hidden("hdnAlfIntelligence", Model.chkBoxAlfIntelligence)
#Html.Label("Toggle ALF Connect users:")
#Html.CheckBoxFor(model => model.chkBoxAlfConnect, new
{
id = "chkFilterAlfConn",
onclick = "filterGrid()",
#checked = "checked"
})
#Html.Hidden("hdnAlfConnect", Model.chkBoxAlfConnect)
#Html.Label("Toggle BRAD users:")
#Html.CheckBoxFor(model => model.chkBoxBrad, new
{
id = "chkFilterBrad",
onclick = "filterGrid()",
#checked = "checked"
})
#Html.Hidden("hdnBrad", Model.chkBoxBrad)
Controller
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using UserManager.Models;
namespace UserManager.Controllers
{
public class UserManagerController : Controller
{
//
// GET: /UserManager/
public ActionResult Index()
{
try
{
var model = new UserManagerTestEntities().vw_UserManager_Model;
return View(model.ToList());
}
catch (Exception ex)
{
return View(ViewBag);
}
}
[HttpPost]
public ActionResult Index(int alfConnect, int alfIntelligence, int brad)
{
List<UserManager.Models.vw_UserManager_Model> modelList = DAL.getGrid(alfConnect, alfIntelligence, brad);
switch (alfConnect)
{
case 1:
ViewData["chkBoxAlfConnect"] = 1;
break;
case 0:
ViewData["chkBoxAlfConnect"] = 0;
break;
}
switch (alfIntelligence)
{
case 1:
ViewData["chkBoxAlfIntelligence"] = 1;
break;
case 0:
ViewData["chkBoxAlfIntelligence"] = 0;
break;
}
switch (brad)
{
case 1:
ViewData["chkBoxBrad"] = 1;
break;
case 0:
ViewData["chkBoxBrad"] = 0;
break;
}
return PartialView("~/Views/Partial/_WebGridUserManager.cshtml", modelList);
}
[ChildActionOnly]
public ActionResult _WebGridFilter ()
{
UserManager.Models.webgrid_validation model = new webgrid_validation();
return PartialView("~/Views/Partial/_WebGridFilter.cshtml", model);
}
//public ActionResult _WebGridFilter(int alfConnect, int alfIntelligence, int brad)
//{
// UserManager.Models.webgrid_validation model = new webgrid_validation();
//switch (alfConnect)
//{
// case 1:
// model.chkBoxAlfConnect = true;
// break;
// case 0:
// model.chkBoxAlfConnect = false;
// break;
//}
//switch (alfIntelligence)
//{
// case 1:
// model.chkBoxAlfIntelligence = true;
// break;
// case 0:
// model.chkBoxAlfIntelligence = false;
// break;
//}
//switch (brad)
//{
// case 1:
// model.chkBoxBrad = true;
// break;
// case 0:
// model.chkBoxBrad = false;
// break;
//}
// return PartialView("~/Views/Partial/_WebGridFilter.cshtml", model);
//}
#region ajaxMethods
public ActionResult LookUpGroupName(string q, int limit)
{
try
{
//TODO: Map list to autocomplete textbox control
DAL d = new DAL();
List<string> groups = d.groups();
var GroupValue = groups
.Where(x => x.Contains(q))
.OrderBy(x => x)
.Take(limit)
.Select(r => new { group = r });
// Return the result set as JSON
return Json(GroupValue, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
return View(ex.ToString());
}
}
public ActionResult LogUserOff(string userid)
{
try
{
//TODO: Map list to autocomplete textbox control
return View();
}
catch (Exception ex)
{
return View(ex.ToString());
}
}
//[HttpPost]
//public ActionResult FilterGrid(int alfConnect, int alfIntelligence, int brad)
//{
// List<UserManager.Models.vw_UserManager_Model> modelList = DAL.getGrid(alfConnect, alfIntelligence, brad);
// return PartialView("_WebGridUserManager", modelList);
//}
#endregion
#region crud
public ActionResult CreateUser()
{
//var data = new UserManager.Models.UserManagerTestEntities();
ViewBag.Message = "Create New User";
var model = new vw_UserManager_Model();
return View(model);
}
[HttpPost]
public ActionResult CreateUser (vw_UserManager_Model model)
{
int outcome = 0;
if (ModelState.IsValid)
{
//var data = new UserManager.Models.UserManagerTestEntities();
// Pass model to Data Layer
outcome = UserManager.DAL.CreateUser(model, outcome);
//data.SaveChanges();
}
if (outcome > 0) // Success
{
return RedirectToAction("showSuccess", model);
}
else // Failure
{
return RedirectToAction("showError", new { ErrorMessage = "Error" });
}
}
public ActionResult EditUser(Guid userid, string salutation, string firstname, string lastname, string password, Nullable<bool> isactive, Nullable<int> maxconcurrentusers, string email, string module, string group)
{
vw_UserManager_Model editUser = new vw_UserManager_Model();
editUser.userid = userid;
editUser.salutation = salutation;
editUser.firstname = firstname;
editUser.lastname = lastname;
editUser.password = password;
editUser.isactive = isactive;
editUser.MaxConcurrentUsers = maxconcurrentusers;
editUser.email = email;
editUser.module_name = module;
editUser.group_name = group;
return View(editUser);
}
[HttpPost]
public ActionResult EditUser(vw_UserManager_Model editUser)
{
if (ModelState.IsValid)
{
UserManager.DAL.EditUser(editUser);
}
return View();
}
public ActionResult DeleteUser(Guid userid, string username, string salutation, string firstname, string lastname, string password, bool isactive, string email, string module, string group)
{
vw_UserManager_Model DeleteUser = new vw_UserManager_Model();
DeleteUser.userid = userid;
DeleteUser.UserName = username;
DeleteUser.salutation = salutation;
DeleteUser.firstname = firstname;
DeleteUser.lastname = lastname;
DeleteUser.password = password;
DeleteUser.isactive = isactive;
DeleteUser.email = email;
DeleteUser.module_name = module;
DeleteUser.group_name = group;
return View(DeleteUser);
}
[HttpPost]
public ActionResult DeleteUser(vw_UserManager_Model deleteUser)
{
if (ModelState.IsValid)
{
UserManager.DAL.DeleteUser(deleteUser);
return RedirectToAction("showSuccess", new { SuccessMessage = "Success" });
}
return View();
}
#endregion crud
#region successErrorHandling
public ActionResult showError(List<string> ErrorMessage)
{
ViewBag.ErrorMessage = ErrorMessage[0];
return View("ErrorMessageView");
}
public ActionResult showSuccess(vw_UserManager_Model model)
{
return View("SuccessMessageeView", model);
}
#endregion
}
}
My question is how to use hidden fields to maintain state of checkboxes between page request?
you could remove the form and send data to the controller as JSon values, and thus refresh only the part of the page that needs to be refreshed.
Regards
below is the code somehow client side validation is not working...I searched couple of questions in this forum and wrote this..
here is the custom validation attribute "startDateAttribute"
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class StartDateAttribute : ValidationAttribute, IClientValidatable
{
public StartDateAttribute ()
{
}
public override bool IsValid(object value)
{
var date = (DateTime)value;
if (date.Date >= DateTime.Now.Date)
{
return true;
}
return false;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
yield return new ModelClientValidationRule
{
ErrorMessage = this.ErrorMessage,
ValidationType = "DateRange"
};
}
}
[CurrentDateAttribute(ErrorMessage = "select the correct date")]
public DateTime? StartDate { get; set; }
here is the JQuery code added
jQuery.validator.addMethod('DateRange', function (value, element, params) {
var d = new Date();
var currentDate = (d.getMonth()+1) + "/"+d.getDate()+ "/" + d.getFullYear() ;
return value >= currentDate;
});
// and an unobtrusive adapter
jQuery.validator.unobtrusive.adapters.add('DateRange', { }, function (options) {
options.rules['DateRange'] = true;
options.messages['DateRange'] = options.message;
});
One of the requirements of client side validation is that the ValidationType and the adapter name should match and should be lower case.
Change the ValidationType and adapter name to 'daterange' and check
For setting that I use Html helper method which is not the best imo, because I use static field.
public enum CurrentState
{
BeforeCurrent,
AfterCurrent
}
public static CurrentState currentState = CurrentState.BeforeCurrent;
public static MvcHtmlString ActiveActionLink(this HtmlHelper helper, string linkText, string actionName, string controllerName, bool checkAction = true)
{
string currentAction = helper.ViewContext.RouteData.GetRequiredString("action");
string currentController = helper.ViewContext.RouteData.GetRequiredString("controller");
if ((controllerName == currentController) && checkAction && (actionName == "Index"))
{
currentState = CurrentState.BeforeCurrent;
}
if ((controllerName == currentController) && checkAction && (actionName != currentAction))
{
if (currentState == CurrentState.BeforeCurrent)
{
return helper.ActionLink(linkText, actionName, controllerName, null, new { #class = "beforeCurrent" });
}
else if (currentState == CurrentState.AfterCurrent)
{
return helper.ActionLink(linkText, actionName, controllerName, null, new { #class = "afterCurrent" });
}
}
if ((controllerName == currentController) && (!checkAction || (actionName == currentAction)))
{
currentState = CurrentState.AfterCurrent;
return helper.ActionLink(linkText, actionName, controllerName, null, new { #class = "current" });
}
return helper.ActionLink(linkText, actionName, controllerName);
}
I have two levels of menus and that's why I use checkAction parameter:
main menu - #Html.ActiveActionLink(Resources.Global.mainMenuBoard, "Index", "Board", checkAction: false)
side menu - #Html.ActiveActionLink(#Resources.Global.managementOverview, "Index", "Management")
and in side menu I need to know if it's after and before current (overlapping items...).
Is it a way to improve that?
Additionally I must say that I use javascript also for that but it must work also for javascript disabled.
I finally solve that by generating whole menu in one helper:
public class Link
{
public string LinkText { get; set; }
public string ActionName { get; set; }
}
public static List<MvcHtmlString> SubMenuLinks(this HtmlHelper helper, string controllerName, List<Link> links)
{
List<MvcHtmlString> menuElements = new List<MvcHtmlString>();
string actualCssClass = "beforeCurrent";
string currentAction = helper.ViewContext.RouteData.GetRequiredString("action");
string currentController = helper.ViewContext.RouteData.GetRequiredString("controller");
foreach (Link link in links)
{
if (controllerName == currentController && link.ActionName == currentAction)
{
menuElements.Add(helper.ActionLink(link.LinkText, link.ActionName, controllerName, null, new { #class = "current" }));
actualCssClass = "afterCurrent";
}
else
{
menuElements.Add(helper.ActionLink(link.LinkText, link.ActionName, controllerName, null, new { #class = actualCssClass }));
}
}
return menuElements;
}
and in view:
#{
List<MvcHtmlString> actionMenu = Html.SubMenuLinks("Manager", new List<Link>()
{
new Link() { LinkText = "linkText", ActionName = "actionName" },
new Link() { LinkText = "linkText2", ActionName = "actionName2" }
});
}
#section SideMenu
{
#for (int i = 0; i < actionMenu.Count; i++)
{
<li id="menu#(i)">#actionMenu.ElementAt(i)</li>
}
}
Not perfect, but it works at least.