How to implement UIHint attribute in mvc - c#

I have gone through a couple of articles regarding UIHint attribute in mvc but none of them specifies clearly what it is or advantages of using it and also how to implement it in code.
Thanks!

The benefit of "UIHint" attribute is that it allows you to selectively apply the template. Let's say that you create an editor template for bool. If you name it "bool" it will automatically apply on all bool fields. But if you name it "abc123" you have the luxury of calling it selectively by decorating the property in your model with the "UIHint" attribute and only having it replace the default handlers as you desire.
It is easiest to understand that all types have a default handler so when you're using "TextboxFor", that too is applying a template, it just happens to be a native one as opposed to a custom template of your choosing.

Related

Apply SuppressImplicitRequiredAttributeForNonNullableReferenceTypes only to particular paths

Can you apply the SuppressImplicitRequiredAttributeForNonNullableReferenceTypes option to only a particular path pattern, e.g., .../v3/...?
We've gone through the trouble of enabling nullable contexts throughout our code, and ensuring all our parameters have the correct nullability. Now we want to utilize that for API validation. But since we don't want break any of our exising API behavior, we only want to apply the implicit Required attribute behvaior on paths for particular API versions. I.e., v2 would NOT have the validation, but v3+ would.
Is there any way to do this?
I can show you the way, but you have to walk through it and complete the implementation!
Ok, to see how SuppressImplicitRequiredAttributeForNonNullableReferenceTypes works, let's check the source code first!
Here's when the parameter is being used in DataAnnotationsMetadataProvider class.
https://github.com/dotnet/aspnetcore/blob/3ea008c80d5cc63de7f90ddfd6823b7b006251ff/src/Mvc/Mvc.DataAnnotations/src/DataAnnotationsMetadataProvider.cs#L343
Now let's see where DataAnnotationsMetadataProvider is used! It is added as a ModelMetadataDetailsProviders to MvcOptions.ModelMetadataDetailsProviders.
https://github.com/dotnet/aspnetcore/blob/3ea008c80d5cc63de7f90ddfd6823b7b006251ff/src/Mvc/Mvc.DataAnnotations/src/DependencyInjection/MvcDataAnnotationsMvcOptionsSetup.cs#L54
So you would need to create your own CustomValidationMetadataProvider and add it to MvcOptions.
builder.Services.AddControllers(op =>
{
op.ModelMetadataDetailsProviders.Add(new CustomValidationMetadataProvider());
});
public class CustomValidationMetadataProvider : IValidationMetadataProvider
{
public void CreateValidationMetadata(ValidationMetadataProviderContext context)
{
// context.ValidationMetadata.IsRequired = ???
}
}
Here you can have your own logic to set context.ValidationMetadata.IsRequired. Unfortunately I'm not sure if you can access the request path here, but you do have access to attributes on the model. So theoretically you could add an attribute to the models on your v3.
There are a few things that I could suggest here:
If you on C# 8 you can try nullable properties/fields for places where you want to allow nullable values. (recommended simplest one)
You can use custom Parameter Binding (non-trivial approach). You can find more details here https://www.strathweb.com/2013/04/asp-net-web-api-parameter-binding-part-1-understanding-binding-from-uri/
You can disable standard model validation and provide your own (where you should be able to specify path) and again non-trivial approach.

Not getting data-val attributes for GUID

I'm working on a bigger project and we have many views and almost all of them have a SelectList or more, whose value is a GUID. The viewmodel works fine and server side validation as well, the problem is that the HTML select element does not get any data-val attributes, we are using Html.DropDownListFor. It works fine when the value is short, string etc but not GUID.
Is there a way to get data-val attributes without adding an ValidationAttribute to all GUID properties in the viewmodels? Because there are a loot of them.
What worked for me in the end:
I got on the right track with Stephen Muecke's answer: we are using our own RequiredAttribute:
public class LocalizedRequiredAttribute : RequiredAttribute
{
public LocalizedRequiredAttribute()
{
ErrorMessageResourceName = "Required";
ErrorMessageResourceType = typeof (Resources.ErrorMessages);
}
}
But this does not add any client side validation attributes like the basic [Required] does, but it was easy to fix. Just add this code to your Application_Start():
DataAnnotationsModelValidatorProvider.RegisterAdapter(
typeof (LocalizedRequiredAttribute),
typeof (RequiredAttributeAdapter));
And now you will get data-val=true data-val-required="message".
Solution found here: https://stackoverflow.com/a/12573540/1225758
No data-val-* attributes are rendered because there are no jquery.validate.unobtrusive adaptors for GUID. The only one you could get out of the box is data-val-required (and the associated data-val) if you were to make the property nullable and add the [Required] attribute.
If you want some client validation you could use a [RegularExpression] attribute (not tested but I think ^[A-Za-z0-9]{8}-[A-Za-z0-9]{4}-[A-Za-z0-9]{4}-[A-Za-z0-9]{4}-[A-Za-z0-9]{12}$ should work).
However it seems unnecessary since you are using #Html.DropDownListFor() and (I assume) your building a SelectList on the controller which would contain only GUID's for the SelectListItem.Value property (why would you render an option which is not valid - other than perhaps a "--Please select--" label option for use with a [Required] attribute?).
Firstly, I would want to know what the problem is with having multiple GUID data annotations on your model properties?
Secondly I would say that it is far clearer and readable to other developers working on the project to have explicit validation going on with data annotations on each property than it is to have some "non-standard" validation voodoo going on.
You could probably achieve this with action filters (look for any properties of type Guid) but I think this will make the use/specification of your models less expressive of their intentions and simply confuse.
Guids have dashes in content and may occur problems. You may try to use .ToString('N') at the end of the Guids where they are being generated to remove dashes. Or you may write a jquery hack to add escape characters before dashes on client side. Or even more; try to implement your own guid validation approach as here: How to test valid UUID/GUID?
Or even even more, you can try to implement your own guid annotation attribute as here:
Validation of Guid

Custom Data Annotations ( DataType )

Is it possible to add new datatypes to the existing DataAnnotations (I'm not looking for a validator but a raw data type). For example
Currnetly you have
[DataType(DataType.Html)]
public string Footer {get; set;}
And into the mix you can add ~Views/Shared/EditorTemplates/Html.cshtml
I'd like to be able to add [DataType(DataType.CSS)] I know in theory I could use a UIHint for adding a specific view, but if possible I'd like to do it at an even earlier stage and specify the datatype rather than relying on UI Hints.
Any pointers would be greatly appreciated. A Quick search of S.O seems a lot of answers around Custom meta-data types, custom validators, and multiple datatyps but I can't seem to find one for adding a new core data-type.
DataType has a second constructor that takes a string. However, internally, this is actually the same as using the UIHint attribute.
Adding a new core DataType is not possible since the DataType enumeration is part of the .NET framework. The closest thing you can do is to create a new class that inherits from the DataTypeAttribute. Then you can add a new constructor with your own DataType enumeration.
public NewDataTypeAttribute(DataType dataType) : base(dataType) { }
public NewDataTypeAttribute(NewDataType newDataType) : base (newDataType.ToString()) { }
Yes, you can. DataTypeAttribute has a constructor that accepts string.

View model properties has changing validation rules at run time

I'm new to C# MVC and I'm trying to add some dynamic validation checks to my view models that are used in a form. For example, I have a string property called FirstName. I can add the attribute StringLength(10) and Required() to it.
My problem is, depending on some other field, the FirstName StringLength could vary from 10 to 20, etc. I still want to use the MVC validations but be able to modify it. I know that attributes are bound to the class so maybe I'm using the wrong thing.
I want the abilities for attribute validation but have it modifiable at run time. Is this possible?
The values in an attribute have to be literals. You can still use attribute based validation, but you will need to use the CustomValidation tag and point it at a method to use. If it depends on multiple fields in the object, you will want to put this on the class rather than the property.
It seems you can add validation attributes at runtime by implementing DataAnnotationsModelValidatorProvider:
Dynamic Attributes # forums.asp.net

Adding and Removing Data Annotations from code

Is there away to add and remove DataAnnotations, in particular the [requried], from the code side of things? My problem is that I want to give the user the ability to save an incomplete form in our CRUD applications but at the same time use the power of the DataAnnotations validation.
If this is not possible, what is the best way I can go about this?
You can keep the DataAnnotation attributes on your model and then just manually clear the validation errors as needed from code. It might look something like this:
if (certainCondition == true) {
ModelState["someKey"].Errors.Clear();
ModelState["anotherKey"].Errors.Clear();
}
It is impossible to add, remove or modify DataAnnotations dynamically since they are Attributes. Attributes are part of the type and can't be changed during runtime.
You could use ModelState as Larsenal suggested provided that:
you use it After validation has executed. (prior to that, ModelState will be empty. It doesn't provide access to all validators, it only stores validator-errors after they've occurred)
you don't have any clientside validation that's based on the DataAnnotationValidators and fires errors that prevent you from even reaching the serverside validation.

Categories

Resources