How to render a menu with Razor using an Editor-Template?

174 views Asked by At

I have an asp.net core 3.1 based app. I like to utilize Display-Templates and Editor-Templates to create the editor views. I am trying to figure out the best approach on how to create editor templates for menus using editor-templates.

My first thought is to create a generic view-model for for menus which then can easily be rendered using a custom editor-template

In a first attempt, I Created a generic viewmodel that looks like this

public abstract class MenuViewModel
{
    [BindNever]
    public IEnumerable<SelectListItem> Options { get; set; }

    public MenuViewModel()
    {
        Options = new List<SelectListItem>();
    }
}

public class MenuViewModel<T> : MenuViewModel
{
    public T Value { get; set; }
}

Then I am hoping to use it in other view-models like this

public class CreateLocation
{
    public string Title { get; set; }

    public MenuViewModel<string> State { get; set; }
}

I think I can get the above code working... However, I am trying to figure out a way to pass any ValiationAttributes from the State property down to the State.Value property instead.

For example, what if the State is decorated with RequiredAttribute? In other words, what if my view-model looks like this

public class CreateLocation
{
    public string Title { get; set; }

    [Required]
    public MenuViewModel<string> State { get; set; }
}

In this case, for the model-state to be valid the State Property cannot be null and the property State.Value must also have a value..

Question is, how can I pass my required attribute down to the Value property without having to create multiple menu-vm like RequiredMenuViewModel and OptionalMenuViewModel?

I thought about creating an implementation of the IDisplayMetadataProvider. But even then, I am not sure how to pass the validation attribute from the parent property to the child.

public class MenuMetadataProvider : IDisplayMetadataProvider
{
    public void CreateDisplayMetadata(DisplayMetadataProviderContext context)
    {
        if (!context.Key.ModelType.IsClass)
        {
            return;
        }

        if(context.Key.ModelType.IsAssignableFrom(typeof(MenuViewModel)))
        {
            metadata.TemplateHint = nameof(MenuViewModel);
        }

        // get any validation attributes on the parent
        var attributes = context.Key.ModelType.GetCustomAttributes(typeof(ValidationAttribute), false);

        if (!attributes.Any())
        {
            // Parent has not attributes, ignore it
            return;
        }

        // iterate over the properties of the child object
        foreach (PropertyInfo childPoperty in context.Key.ModelType.GetProperties())
        {
            // find a child property that is decorated with InheritAttributesFromParentAttribute
            var canInherit = Attribute.IsDefined(childPoperty, typeof(InheritAttributesFromParent));

            if (!canInherit)
            {
                continue;
            }

            // some how pass the attributes to the childPoperty
        }
    }
}

0

There are 0 answers