Getting property attributes in TagHelpers
Asked Answered
H

3

10

Some of model properties has "Required" data annotation, that I need to read in a TagHelper class.

public partial class Sale
{
    [Required]
    public string CustomerId { get; set; }
    ...

In the sales view I create a custom select for customer:

<customer asp-for="CustomerId " value="@Model.CustomerId"></customer>

And in the CustomerTagHelper class there is the process method:

public override void Process(TagHelperContext context, TagHelperOutput output)
{

How can I discover at this point, if the current bind property has the "required" attribute? I´m using asp-net core.

Heaume answered 9/11, 2016 at 12:18 Comment(0)
C
7

The tag helper doesn't know about anything other than what you provide as input for its attributes. So you want to create a tag helper that you can use as follows:

@model WebApplication4.Models.Sale
...
<customer asp-for="CustomerId" />

Then you would declare a property of type ModelSource associated with the asp-for attribute. That would give you access to not just the value of the property but also metadata like the following (and more!):

  • property value: source.Model
  • property name: source.Name
  • container model type: source.Metadata.ContainerType
  • IsRequired flag: source.Metadata.IsRequired

You will also get intellisense in VS to select one of properties in your model for the asp-for model and it will throw an error if the value isnt the name of a model property.


As an example, take a look at this tag helper:

public class CustomerTagHelper: TagHelper
{
    [HtmlAttributeName("asp-for")]
    public ModelExpression Source { get; set; }

    public override void Process(TagHelperContext context, TagHelperOutput output)
    {
        output.TagName = "p";
        output.TagMode = TagMode.StartTagAndEndTag;

        var contents = $@"
            Model name: {Source.Metadata.ContainerType.FullName}<br/>
            Property name: {Source.Name}<br/>
            Current Value: {Source.Model}<br/> 
            Is Required: {Source.Metadata.IsRequired}";

        output.Content.SetHtmlContent(new HtmlString(contents));
    }
}

Then if you had these 2 models:

public class Sale
{
    [Required]
    public string CustomerId { get; set; }
}
public class Promotion
{        
    public string CustomerId { get; set; }
}

Which are used in these 2 actions and views:

public IActionResult Sale()
{
    return View();
}

@model WebApplication4.Models.Sale
...
<customer asp-for="CustomerId" />


public IActionResult Promotion()
{
    return View(new Models.Promotion { CustomerId = "abc-123" });
}

@model WebApplication4.Models.Promotion
...
<customer asp-for="CustomerId" />

Will produce these outputs:

Tag helper for: WebApplication4.Models.Sale
Property name: CustomerId
Current Value: 
Is Required: True

Model name: WebApplication4.Models.Promotion
Property name: CustomerId
Current Value: abc-123
Is Required: False
Carousel answered 9/11, 2016 at 15:9 Comment(4)
Instead of "Source.Metadata.IsRequired" I use "For.Metadata.IsRequired". Works.Heaume
Yep, whatever name you used fpr the property of type ModelExpressionCarousel
How about accessing other attributes?Alfieri
Take a look at Source.Metadata as it gives you access to more information about that particular model property, including its type and its containing typeCarousel
O
10

You can access custom attribute through ModelExpression.

public class CustomTagHelper : TagHelper
{
    [HtmlAttributeName("asp-for")]
    public ModelExpression For { get; set; }

    public override void Process(TagHelperContext context, TagHelperOutput output)
    {            
        CustomAttribute attribute = For.Metadata
                                       .ContainerType
                                       .GetProperty(For.Name)
                                       .GetCustomAttribute(typeof(CustomAttribute)) 
                                       as CustomAttribute;
        if (attribute != null)
        {
            output.Attributes.Add("some-attr", attribute.Text);
        }
    }
}

And then just use it in your template <custom asp-for="SomeProp"></custom>.

Our answered 19/9, 2017 at 13:53 Comment(1)
Looks like the attribute name has to be [HtmlAttributeName("asp-for")] using [HtmlAttributeName("for")]does not work for me ASP.NET core 2.2Bedard
C
7

The tag helper doesn't know about anything other than what you provide as input for its attributes. So you want to create a tag helper that you can use as follows:

@model WebApplication4.Models.Sale
...
<customer asp-for="CustomerId" />

Then you would declare a property of type ModelSource associated with the asp-for attribute. That would give you access to not just the value of the property but also metadata like the following (and more!):

  • property value: source.Model
  • property name: source.Name
  • container model type: source.Metadata.ContainerType
  • IsRequired flag: source.Metadata.IsRequired

You will also get intellisense in VS to select one of properties in your model for the asp-for model and it will throw an error if the value isnt the name of a model property.


As an example, take a look at this tag helper:

public class CustomerTagHelper: TagHelper
{
    [HtmlAttributeName("asp-for")]
    public ModelExpression Source { get; set; }

    public override void Process(TagHelperContext context, TagHelperOutput output)
    {
        output.TagName = "p";
        output.TagMode = TagMode.StartTagAndEndTag;

        var contents = $@"
            Model name: {Source.Metadata.ContainerType.FullName}<br/>
            Property name: {Source.Name}<br/>
            Current Value: {Source.Model}<br/> 
            Is Required: {Source.Metadata.IsRequired}";

        output.Content.SetHtmlContent(new HtmlString(contents));
    }
}

Then if you had these 2 models:

public class Sale
{
    [Required]
    public string CustomerId { get; set; }
}
public class Promotion
{        
    public string CustomerId { get; set; }
}

Which are used in these 2 actions and views:

public IActionResult Sale()
{
    return View();
}

@model WebApplication4.Models.Sale
...
<customer asp-for="CustomerId" />


public IActionResult Promotion()
{
    return View(new Models.Promotion { CustomerId = "abc-123" });
}

@model WebApplication4.Models.Promotion
...
<customer asp-for="CustomerId" />

Will produce these outputs:

Tag helper for: WebApplication4.Models.Sale
Property name: CustomerId
Current Value: 
Is Required: True

Model name: WebApplication4.Models.Promotion
Property name: CustomerId
Current Value: abc-123
Is Required: False
Carousel answered 9/11, 2016 at 15:9 Comment(4)
Instead of "Source.Metadata.IsRequired" I use "For.Metadata.IsRequired". Works.Heaume
Yep, whatever name you used fpr the property of type ModelExpressionCarousel
How about accessing other attributes?Alfieri
Take a look at Source.Metadata as it gives you access to more information about that particular model property, including its type and its containing typeCarousel
R
1

You can do it like this:

    var type = typeof(YOUR_CLASS);
    var property = type.GetProperty("FIELD_NAME");
    var isRequired = Attribute.IsDefined(property, typeof(Required));
Rayon answered 9/11, 2016 at 13:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.