Florim Maxhuni
Florim Maxhuni

Reputation: 1421

Extend Html.Editor()

I need to extend Html.Editor() so base on some attributes in model to generate HTML. Example:

public class Person
{
    public string Name { get; set; }

    [DisplayFor(Role.Admin)]
    public string Surname { get; set; }
}

In this case the generated HTML will not be displayed in View if user is different from admin.

Upvotes: 1

Views: 1658

Answers (2)

Darin Dimitrov
Darin Dimitrov

Reputation: 1038800

Here's a sample implementation. Let's assume that you have defined the following attribute:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class DisplayForAttribute : Attribute
{
    public DisplayForAttribute(string role)
    {
        Role = role;
    }
    public string Role { get; private set; }
}

Next you could write a custom metadata provider which will use this attribute:

public class MyMetadataProvider : DataAnnotationsModelMetadataProvider
{
    protected override ModelMetadata CreateMetadata(
        IEnumerable<Attribute> attributes,
        Type containerType, 
        Func<object> modelAccessor, 
        Type modelType, 
        string propertyName
    )
    {
        var metadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);
        var displayFor = attributes.OfType<DisplayForAttribute>().FirstOrDefault();
        if (displayFor != null)
        {
            metadata.AdditionalValues.Add("RequiredRole", displayFor.Role);
        }
        return metadata;
    }
}

which will be registered in Application_Start:

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();

    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);

    ModelMetadataProviders.Current = new MyMetadataProvider();
}

and the last part is to write a custom editor template for the String type (~/Views/Shared/EditorTemplates/String.cshtml):

@{
    var visible = false;
    if (ViewData.ModelMetadata.AdditionalValues.ContainsKey("RequiredRole"))
    {
        var role = (string)ViewData.ModelMetadata.AdditionalValues["RequiredRole"];
        visible = User.IsInRole(role);
    }
}
@if (visible)
{
    @Html.TextBox(
        "", 
        ViewData.TemplateInfo.FormattedModelValue,
        new { @class = "text-box single-line" }
    )
}

and lastly to use the attribute:

public class MyViewModel
{
    [DisplayFor("Admin")]
    public string Name { get; set; }
}

and in the view:

@using (Html.BeginForm())
{
    @Html.EditorFor(model => model.Name)
    <input type="submit" value="OK" />
}

Obviously this covers only the string editor template but the example could be easily extended to the other default templates including display templates as well.

Upvotes: 2

paztulio
paztulio

Reputation: 351

I have the same stuff in my project, but the code is at work.

I wrote an extension method for ModelMetadata or / and PropertyInfo

public static bool IsVisibleForRole( this PropertyInfo property, User c);

In my Object.ascx:

for ( var field in fields ) {
     if(!field.IsVisibleForRole(this.CurrentUser())) continue;
     //...
}

Though, in your case, you might not skip the field, but insert a <input type="hidden"> instead. But this might be security issue. For details, have look at: http://www.codethinked.com/aspnet-mvc-think-before-you-bind

Upvotes: 0

Related Questions