Enable Display Format annotation from a ViewModel - c#

I am trying to develop an application using MVC 5 and EF 6 code first approach.
I am new at software devolpment.
I would like to add this data annotation:
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
to a property named Birthdate from a ViewModels
An Example to illustrate:
public class StudentRegisterViewModel
{
public string LastName { get; set; }
public string FirstMidName { get; set; }
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime EnrollmentDate { get; set; }
}
The problem here is that the field EnrollmentDate does not recognize DisplayFormat annotations.
Can anyone help me, to handle it, please?

The [DisplayFormat] attribute is only used by DisplayFor and EditorFor. And not by the other helpers like TextBoxFor.
To format it further, you can create a css class and use jQuery to apply it:
$("EnrollmentDate").addClass("class-name")

To apply the format specified in [DisplayFormat] you need to access the value via Html.DisplayFor(). For example, in a view that would be:
#Html.DisplayFor(vm => vm.EnrollmentDate)

Write your own ValidationAttribute. To do this you override the IsValid() method with your own logic.
Add more validation if you wish:
public class MyAmazingValidationClass : ValidationAttribute
{
protected override bool IsValid(object value)
{
DateTime date;
bool parsed = DateTime.TryParse((string)value, out date);
//or maybe :
bool parsed = DateTime.ParseExact((string)value),"dd/MM/yyyy")
if(!parsed)
return false;
return true;
}
}
Then go to your awesome class and decorate it:
public string FirstMidName { get; set; }
[DataType(DataType.Date)]
[MyAmazingValidationClass(ErrorMessage="You made a big mistake right now!")]
public DateTime EnrollmentDate { get; set; }

Related

ASP.NET CORE DataFormat is effecting Edit action

In my project I have properties that I would like to be displayed in dollar format.In my class I have it set up to do so
public class Worker{
public int Id {get;set;}
public string Name {get;set;}
[DisplayFormat(DataFormatString = "{0:C0}", ApplyFormatInEditMode = true)]
public double Rev {get;set;}
[DisplayFormat(DataFormatString = "{0:C0}", ApplyFormatInEditMode = true)]
public double Cost {get;set;}
[DisplayFormat(DataFormatString = "{0:C0}", ApplyFormatInEditMode = true)]
public double Profit {get;set;}
}
This works and displays properly however when I go to the edit page All of the info is displayed in this format and I have to change every field from the dollar sign to the double or I get an error. Here is a screenshot showing.
Even if I want to change one field I have to rewrite every field from $50,000 to 50000.00. How can I fix it so in Edit I don't have to worry about fixing every field, and my dollar sign format will be displayed in Index or Details and so on? Here is my controller for Edit
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var worker = await _context.Workers.FindAsync(id);
if (worker == null)
{
return NotFound();
}
return View(worker);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("Id,Rev,Cost,Profit")] Worker worker)
{
if (id != worker.Id)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(worker);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!WorkerExists(worker.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(worker);
}
At this point I am not concerned whether the currency format is displayed in edit view or just a number. I just do not want to have to convert every field from currency to a number in order to save the updates on one single property. How can I get rid of this error? Either leaving in this format or in number format.
Even if I want to change one field I have to rewrite every field from $50,000 to 50000.00. How can I fix it so in Edit I don't have to worry about fixing every field, and my dollar sign format will be displayed in Index or Details and so on?
IMO, you just need to set ApplyFormatInEditMode=false or remove it from model.
public class Worker
{
public int Id { get; set; }
public string Name { get; set; }
[DisplayFormat(DataFormatString = "{0:C0}", ApplyFormatInEditMode = false)]
public double Rev { get; set; }
[DisplayFormat(DataFormatString = "{0:C0}", ApplyFormatInEditMode = false)]
public double Cost { get; set; }
[DisplayFormat(DataFormatString = "{0:C0}", ApplyFormatInEditMode = false)]
public double Profit { get; set; }
}

Validating form when model is a collection of sub models

I've got a view which needs several models to work correctly. So, I created a model which is a collection of multiple (sub) models. This is the model.
public class PolicyDetail
{
public Policy Policy { get; set; }
public IEnumerable<Insured> Insureds { get; set; }
public IEnumerable<Risk> Risks { get; set; }
public IEnumerable<Construction> Constructions { get; set; }
}
And here's an example of what one of the sub models look like, which is an actual entity from the database:
public class Policy
{
[Key]
public int PolicyID { get; set; }
[DisplayName("Policy Number")]
public Guid PolicyNumber { get; set; }
[Required(ErrorMessage = "Please enter a valid Effective Date.")]
[DataType(DataType.DateTime)]
[DisplayName("Effective Date")]
[DisplayFormat(DataFormatString = "{0:MM/dd/yyyy}", ApplyFormatInEditMode = true)]
public DateTime EffDate { get; set; }
[Required(ErrorMessage = "Please enter a valid Expiration Date.")]
[DataType(DataType.DateTime)]
[DisplayName("Expiration Date")]
[DisplayFormat(DataFormatString = "{0:MM/dd/yyyy}", ApplyFormatInEditMode = true)]
public DateTime ExpDate { get; set; }
public Boolean IsActive { get; set; }
}
This was all working well, right up until I tried to submit a form with errors in it to test the validation. I should have seen this coming (maybe?) but because the actual model doesn't have any validation tags on it, it always passes the if (ModelState.IsValid) check. Is there some way to enforce, or inherit, all of the Data Annotations from the sub classes?
Or, am I going about this all wrong, using a model which is a collection of other models? The thing is, I want to be able to edit/add multiple db entities from the same view.
EDIT:
This article by Josh Carroll looks to be EXACTLY what I need. But when I implement it, I get a Null Object error. Here's what I'm doing:
public class PolicyDetail
{
[Required, ValidateObject]
public Policy Policy { get; set; }
public IEnumerable<Insured> Insureds { get; set; }
public IEnumerable<Risk> Risks { get; set; }
public IEnumerable<Construction> Constructions { get; set; }
}
Then in the override method he provides:
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var results = new List<ValidationResult>();
var context = new ValidationContext(value, null, null);
Validator.TryValidateObject(value, context, results, true);
if (results.Count != 0)
{
var compositeResults = new CompositeValidationResult(String.Format("Validation for {0} failed!", validationContext.DisplayName));
results.ForEach(compositeResults.AddResult);
return compositeResults;
}
return ValidationResult.Success;
}
}
the parameter "value" comes in null, so it errors on this line:
Validator.TryValidateObject(value, context, results, true);
Am I missing something? Doing something wrong?
You can manually call the validations on the sub-models using this: https://msdn.microsoft.com/en-us/library/dd411772.aspx
var context = new ValidationContext(model.Policy, serviceProvider: null, items: null);
var validationResults = new List<ValidationResult>();
bool isValid = Validator.TryValidateObject(model.Policy, context, validationResults, true);
You can then use the ModelState.AddModelError to build the response from that.
Definitely not the most elegant possible solution, but might be easier than rewriting what you have.

Combine custom validation attribute with formatting

I have a metadata class for my Customer where I validate the PurchaseDate.
The first annotation (DataType) is for formatting the date in an EditorFor, to show just the date part.
The second annotation is a custom validation to verify that the value is a DateTime, including a custom error message.
My problem is that the first annotation will cancel out the errormessage of the second annotation.
Is it possible to combine these two using only data annotations? Or do I have to format the date in the EditorFor?
[MetadataType(typeof(Customer_Metadata))]
public partial class Customer { }
public class Customer_Metadata
{
[DataType(DataType.Date)]
[MyDate(ErrorMessage = "Invalid purchase date")]
public DateTime? PurchaseDate { get; set; }
}
The same problem occurs if I try to replace the [DataType(DataType.Date)]
with
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
I won't get my custom error message.
EDIT
My main goal is to have a custom error message while also only showing the date part in the rendered input field. Is it possible with only data annotations?
Here's the MyDate attribute:
public class MyDate : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
DateTime dt;
var test = DateTime.TryParse((value ?? string.Empty).ToString(), out dt);
if (test)
{
return ValidationResult.Success;
}
else
{
return new ValidationResult(ErrorMessage);
}
}
}

ASP.Net MVC Web does not validate empty fields

I use Visual Studio 2013, Update 5
with the ASP.NET MVC Project Template I created a simple Web to get deeper into MVC after taking a few steps.
I added a Model for which i creaded a standard Edit View and Controller.
In my Model I use "Required" and MinLength Attributes.
The MinLength Attibutes raised Validation Messages but if the Fields are kept empty the "Required" Attributes doesn't work.
What works is, if I just put one Character in the field, and leave the field (so that the MinLenth Validation Fails) and afterwards Clear the complete field.
Only in this Case, the Required Attribute seems to do anythings.
(Bug or Feature?! :-) )
Here is the Model I use with the Edit View completely generated by "new View" Template of Visual Studio
public class Apotheke
{
[DisplayName("Apotheken Nr.")]
[DisplayFormat(DataFormatString = "{0:D4}")]
[Range(1, 9999)]
[Required(AllowEmptyStrings = false)]
public int ApothekenNr { get; set; }
[DisplayName("Name der Apotheke")]
[MinLength(3)]
[Required(AllowEmptyStrings = true)]
public string ApoName { get; set; }
[MinLength(3)]
[Required(AllowEmptyStrings = false)]
public string Straße { get; set; }
[MinLength(5)]
[MaxLength(5)]
[Required(AllowEmptyStrings = false)]
public string PLZ { get; set; }
[MinLength(3)]
[Required(AllowEmptyStrings = false)]
public string Ort { get; set; }
[DisplayName("Inhaber Vorname")]
[MinLength(3)]
[Required(AllowEmptyStrings = false)]
public string Vorname { get; set; }
[DisplayName("Inhaber Nachname")]
[MinLength(3)]
[Required(AllowEmptyStrings = false)]
public string Nachname { get; set; }
[DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)]
[MinLength(10)]
[MaxLength(10)]
[Required(AllowEmptyStrings = false)]
public DateTime Eintritt { get; set; }
}
AllowEmptyStrings = false raises a validation error when user enters blank spaces. It is working fine in below framework. Please try validating object in controller to make sure your annotations are correct. Check ModelState errors.
To test your issue I am using below packages on .NET 4.5 (VS 2013, MVC 5):
"EntityFramework" version="6.1.3"
"jQuery" version="2.1.4"
"jQuery.Validation" version="1.11.1"
public ActionResult Edit(string id)
{
...
returningModel.PLZ = " ";
//returningModel.PLZ = null;
bool b = TryValidateModel(returningModel);
var modelStateErrors = ModelState.Values.SelectMany(m => m.Errors);
return View(returningModel);
}

Fluent Validation Inconsistent with ASP.NET MVC 5

I'm using Fluent Validation v5.5 with ASP.NET v5.2.2 and I'm getting some inconsistent results with the validation.
My view model is:
public class QuoteViewModel
{
[Display(Name = #"Day")]
public int DateOfBirthDay { get; set; }
[Display(Name = #"Month")]
public int DateOfBirthMonth { get; set; }
[Display(Name = #"Year")]
public int DateOfBirthYear { get; set; }
[Display(Name = #"Gender")]
public Gender? Gender { get; set; }
[Display(Name = #"State")]
public int StateId { get; set; }
}
My controller method is:
public ActionResult Quote(QuoteViewModel viewModel)
{
var _validator = new QuoteValidator();
var results = _validator.Validate(viewModel);
if (!ModelState.IsValid)
{
return Json(false);
}
return Json(true);
}
My validator is:
public class QuoteValidator : AbstractValidator<QuoteViewModel>
{
public QuoteValidator()
{
RuleFor(x => x.Gender).NotEmpty();
RuleFor(x => x.StateId).NotEmpty();
RuleFor(x => x.DateOfBirthDay).NotEmpty().InclusiveBetween(1, 31);
RuleFor(x => x.DateOfBirthMonth).NotEmpty().InclusiveBetween(1, 12);
RuleFor(x => x.DateOfBirthYear).NotEmpty().LessThanOrEqualTo(DateTime.UtcNow.Year);
}
}
I'm running a test that posts all blank value form fields. Thus the view model fields retain default values after the view model object is created.
For comparison, in the controller I'm running the validation explicitly and the results aren't consistent with the validation result in ModelState.
ModelState is showing 4 errors, all triggered by NotEmpty rules. NotEmpty on the nullable enum Gender doesn't seem to trigger.
The explicit validation is returning 7 out of 8 errors, the LessThanOrEqualTo rule won't fire since the DateOfBirthYear defaults to zero.
My pain point is I can't figure out why ModelState is missing the NotEmpty error on the nullable enum Gender.
The only way I've been able to trigger that error is to post just the Gender value.
Please help.
EDIT:
After stepping through some code, it appears that the issue is related to the Fluent Validation RequiredFluentValidationPropertyValidator. The Gender field is a nullable value type which is bound to null. The following snippet from RequiredFluentValidationPropertyValidator prevents validation:
ShouldValidate = isNonNullableValueType && nullWasSpecified;
!ModelState.IsValid doesn't use your validation result it uses defaulf MVC validation (that can be added through DataAnnotations). You have to check !results.IsValid instead which contains the validation result of your QuoteValidator.
If you want to use default ModelState.IsValid you have to mark your model with validator attribute:
[Validator(typeof(QuoteValidator))]
public class QuoteViewModel
{
[Display(Name = #"Day")]
public int DateOfBirthDay { get; set; }
[Display(Name = #"Month")]
public int DateOfBirthMonth { get; set; }
[Display(Name = #"Year")]
public int DateOfBirthYear { get; set; }
[Display(Name = #"Gender")]
public Gender? Gender { get; set; }
[Display(Name = #"State")]
public int StateId { get; set; }
}
And add the following line to your Application_Start method:
protected void Application_Start() {
FluentValidationModelValidatorProvider.Configure();
}

Categories

Resources