ASP.NET MVC Editor-Templates/UIHint with parameters

17,560

Solution 1

You could use the AdditionalMetadata attribute:

[UIHint("DateTime")]
[AdditionalMetadata("foo", "bar")]
public DateTime Date { get; set; }

and in the template:

@ViewData.ModelMetadata.AdditionalValues["foo"]

so if you wanted to pass an url:

[UIHint("DateTime")]
[AdditionalMetadata("controller", "somecontroller")]
[AdditionalMetadata("action", "someaction")]
[AdditionalMetadata("property", "someproperty")]
public DateTime Date { get; set; }

and in your template:

@{
    var values = ViewData.ModelMetadata.AdditionalValues;
}

<script type="text/javascript">
$('.auto').autocomplete({
    source: function (request, response) {
        $.ajax({
            url: '@Url.Action((string)values["action"], (string)values["controller"])',
            dataType: "json",
            data: {
                filter: request.term
            },
            success: function (data) {
                response(
                    $.map(eval(data), function (item) {
                        return {
                            label: item['@values["property"]']
                        }
                    })
                );
            }
        });
    }                    
});
</script>

Solution 2

You could use the UHint without AdditionalMetadata attribute, but some additional code is requred

[UIHint("DateTime", null, "key1", "value1", "key2", "value2")]
public DateTime Date { get; set; }

override CreateMetadata:

public class CustomMetadataProvider : DataAnnotationsModelMetadataProvider
    {
        public const string UiHintControlParameters = "UiHintControlParameters";

        protected override ModelMetadata CreateMetadata(
            IEnumerable<Attribute> attributes,
            Type containerType,
            Func<object> modelAccessor,
            Type modelType,
            string propertyName)
        {

            ModelMetadata metadata = base.CreateMetadata(
                attributes,
                containerType,
                modelAccessor,
                modelType,
                propertyName);

            IEnumerable<UIHintAttribute> uiHintAttributes = attributes.OfType<UIHintAttribute>();
            UIHintAttribute uiHintAttribute = uiHintAttributes.FirstOrDefault(a => string.Equals(a.PresentationLayer, "MVC", StringComparison.OrdinalIgnoreCase))
                                              ?? uiHintAttributes.FirstOrDefault(a => String.IsNullOrEmpty(a.PresentationLayer));
            if (uiHintAttribute != null)
            {
                metadata.AdditionalValues.Add(UiHintControlParameters, uiHintAttribute.ControlParameters);
            }

            return metadata;
        }

Register CustomMetadataProvider:

    public static void Application_Start()
    {
        ModelMetadataProviders.Current = new CustomMetadataProvider();
    }

and in your template:

    @{
        IDictionary<string, object> values = (IDictionary<string, object>)
ViewData.ModelMetadata.AdditionalValues[CustomMetadataProvider.UiHintControlParameters];
    }
Share:
17,560
Rohan Büchner
Author by

Rohan Büchner

I've been writing code since the late 90s, first as a childhood hobby, then transitioning it into my career. I enjoy working in / with: JavaScript (and all of its many tails) .Net AWS (including Infrastructure as Code) Favourite IDEs: Jetbrains anything But can get by with VSCode in a pinch When I'm not programming, you'll find me on: SoundCloud: Where I post infrequently, expect random metal guitar outbursts. Eve &amp; Ro (YouTube): This is a channel where my wife &amp; I post travel things. Blog: Mainly tech related, but I mainly use it as a testbed to try new technologies. I have rebuilt this many times over. Currently it is NEXTJS based. ZaTech (Slack): If you need to message me for any inquiries.

Updated on June 18, 2022

Comments

  • Rohan Büchner
    Rohan Büchner almost 2 years

    I've been using Editor-Templates in the past like this, by applying the following data annotation:

    [UIHint("SomeTemplate")]
     
    

    ViewModel:

     public class MicroViewModel
     {
        public IEnumerable<LabMicro> Micros { get; set; }
    
        [UIHint("DateTime")]
        public DateTime Date { get; set; }
    
        public int CaseNo { get; set; }
    
        [UIHint("SampleTypes")]
        public int LabSampleTypeID { get; set; }
    
        [UIHint("SampleDetails")]
        public int LabSampleDetailID { get; set; }
     }
    

    If I wanted to use a specific date picker control as opposed to the regular one, it can be implemented as follows.

    Example:

    @model DateTime?    
    @Html.TextBox("",  String.Format("{0:yyyy-MM-dd}", Model.HasValue ? 
            Model : DateTime.Today), new { @class = "dp", style="width:100px" })
    
    <script type="text/javascript">    
        $(document).ready(function () {    
            $(".dp").datepicker({    
                changeMonth: true,    
                changeYear: true,
                dateFormat: 'yy-mm-dd'    
            });    
        });      
    </script>  
    

    For my ID fields, I'd like to make use of the jQuery auto-complete component.

    Question:

    How would I go about passing additional parameters to the UIHint partial view for LabSampleTypeID and LabSampleDetailID? (As Id like to have an auto-complete Editor-Template that'll take a URL and property names for example)

    What I think my auto-complete Editor-Template/Partial should look like:

    $(".auto").autocomplete({
        source: function(request, response) {
            $.ajax({
                url: '[#URL_TO_USE]',
                dataType: "json",
                data: {
                    filter: request.term
                },
                success: function(data) {
                    response($.map(eval(data), function(item) {
                        return {
                            label: item.[#PROPERTY_TO_USE]
                        }
                    }));
                }
            })
        }
    });
    
  • Anton Kalcik
    Anton Kalcik over 8 years
    UHint class has constructor with additional params object[] controlParameters. What is the purpose of this?
  • Sel
    Sel over 7 years
    UHint controlParameters seems to be more sutable for this
  • Ciaran Gallagher
    Ciaran Gallagher over 7 years
    Hi I tried your code but on attempting to register the custom provider it says it's unable to convert target type to System.Web.Mvc.ModelMetadataProvider, any ideas?
  • Ciaran Gallagher
    Ciaran Gallagher over 7 years
    The override that we created is extending from 'DataAnnotationsModelMetadataProvider' just like your example, but the 'ModelMetadataProviders.Current' is expecting a ModelMetadataProvider.
  • Sel
    Sel over 7 years
    Ciaran, enshure you override DataAnnotationsModelMetadataProvider from System.Web.Mvc namespace, not System.Web.Http.Metadata.Providers