This question raises after what I've tried from the answer mentioned in my previous question. I followed this article exactly the same way but validations for image files
instead of doc files
mentioned in the article.
Description: I have a input
control of type=file
which is to upload image files and this exists in one of the partialview
. The partialview
gets loaded on click
of a button
. And to apply validations
mentioned in model
, explicitly add unobtrusive
to the form
. But after following all the set-ups mentioned in the above-said article, I am not able to validate the file on submit
also the the data-*
created by unobtrusive validation
is quite fishy or better say invalid. Below is the code to show how my setup looks like and here is the html
which gets created by unobtrusive validation with invalid data-*
attribute, may be because of which the validation fails to happen.
<input data-charset="file" data-val="true" data-val-fileextensions="" data-val-fileextensions-fileextensions="png,jpg,jpeg" id="File" multiple="multiple" name="File" type="file" value="">
Load Partial View Js
$('.getpartial').on('click', function () {
$('.loadPartial').empty().load('/Home/GetView',function () {
var form = $('form#frmUploadImages');
form.data('validator', null);
$.validator.unobtrusive.parse(form);
$(function () {
jQuery.validator.unobtrusive.adapters.add('fileextensions', ['fileextensions'], function (options) {
var params = {
fileextensions: options.params.fileextensions.split(',')
};
options.rules['fileextensions'] = params;
if (options.message) {
options.messages['fileextensions'] = options.message;
}
});
jQuery.validator.addMethod("fileextensions", function (value, element, param) {
var extension = getFileExtension(value);
var validExtension = $.inArray(extension, param.fileextensions) !== -1;
return validExtension;
});
function getFileExtension(fileName) {
var extension = (/[.]/.exec(fileName)) ? /[^.]+$/.exec(fileName) : undefined;
if (extension != undefined) {
return extension[0];
}
return extension;
};
}(jQuery));
})
})
ModelClass
public class ImageUploadModel
{
[FileValidation("png|jpg|jpeg")]
public HttpPostedFileBase File { get; set; }
}
View
@model ProjectName.Models.ImageUploadModel
@using (Html.BeginForm("UploadImages", "Admin", FormMethod.Post, htmlAttributes: new { id = "frmUploadImages", novalidate = "novalidate", autocomplete = "off", enctype = "multipart/form-data" }))
{
<div class="form-group">
<span class="btn btn-default btn-file">
Browse @Html.TextBoxFor(m => m.File, new { type = "file", multiple = "multiple", data_charset = "file" })
</span>
<span class="text-muted" id="filePlaceHolder">No files selected</span>
@Html.ValidationMessageFor(m => m.File, null, htmlAttributes: new { @class = "invalid" })
</div>
<div class="form-group">
<button class="btn btn-primary addImage pull-right">
<i class="fa fa-upload"></i> Upload
</button>
</div>
}
and finally my CustomFileValidation class
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class FileValidationAttribute : ValidationAttribute, IClientValidatable
{
private List<string> ValidExtensions { get; set; }
public FileValidationAttribute(string fileExtensions)
{
ValidExtensions = fileExtensions.Split('|').ToList();
}
public override bool IsValid(object value)
{
HttpPostedFileBase file = value as HttpPostedFileBase;
if (file != null)
{
var fileName = file.FileName;
var isValidExtension = ValidExtensions.Any(y => fileName.EndsWith(y));
return isValidExtension;
}
return true;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientFileExtensionValidationRule(ErrorMessage, ValidExtensions);
yield return rule;
}
}
public class ModelClientFileExtensionValidationRule : ModelClientValidationRule
{
public ModelClientFileExtensionValidationRule(string errorMessage, List<string> fileExtensions)
{
ErrorMessage = errorMessage;
ValidationType = "fileextensions";
ValidationParameters.Add("fileextensions", string.Join(",", fileExtensions));
}
}
You need to move the block code
from inside the
$('.getpartial').on(..)
function to before it so that its isCurrently your load the content, re-parse the validator and then add add the methods to jquery validation but its to late (the validator has already been parsed)
Sidenote: You do not need to wrap the validation functions in
$(function () {
. It can be deleted and simply use$.validator...
instead ofjQuery.validator....
as your doing elsewhere in your code.As for the 'fishy'
data-val-*
attributes, that is exactly what your code generates. Your generating aClientValidationRule
namedfileextensions
(theValidationType = "fileextensions";
code) and then you add a property of it also namedfileextensions
(theValidationParameters.Add("fileextensions", ..)
code which generatesdata-val-fileextensions-fileextensions="png,jpg,jpeg"
. As fordata-val-fileextensions=""
, that is generated to store the error message but you have not generated one so its an empty string.I would suggest a few changes to your code.
FileTypeAttribute
so that you have the flexibility to add other file validation attributes, for exampleFileSizeAttribute
to validate the maximum size.private const string _DefaultErrorMessage = "Only the following file types are allowed: {0}";
and in the last line of the constructor includeErrorMessage = string.Format(_DefaultErrorMessage, string.Join(" or ", ValidExtensions));
ValidationParameters.Add("fileextensions", ...)
to (say)ValidationParameters.Add("validtypes", ...)
so it generatesdata-val-fileextensions-validtypes="png,jpg,jpeg"
which is a bit more meaningful (note you will need to change the script to...add('fileextensions', ['validtypes'], function() ....
Edit
Your code will not work with
<input type="file" multiple="multiple" ... />
In order to do so your property needs to beIEnumerable
(note a few minor changes to your code)Then the validation attribute needs to check each file in the collection
And finally the script needs to check each file