MVC Label Helper customizado para campos obrigatórios

criando extensão para LabelFor no ASP.NET MVC 3 com Razor

Categoria: Posts Técnicos
Compartilhar:     
MVC Label Helper

Hoje iniciarei uma sequência de posts técnicos referentes a experiências que tenho aqui na Light Solutions no setor de desenvolvimento sob medida,

o intuito é registrar essas experiências e ajudar outras pessoas com essas informações, atualmente já ajudo alguns amigos por Skype e no trabalho, então registrarei essas situações aqui no blog da empresa.

para esse primeiro post explicarei uma situação com ASP.NET MVC 3 / Razor / C# que utilizei no projeto Portal Mariaca Online

Caso

para montar um form necessitei de um @Html.LabelFor, mas preciso que os labels apresentem um “*” ao lado dos campos obrigatórios, mas respeitando os DataAnnotations que estão no Model, por exemplo:

[Required(ErrorMessage = "campo obrigatório")]
[Display(Name = "nome")]
public String ClienteSt;

no meu caso, o @Html.LabelFor(m => m.ClienteSt) deveria renderizar:

<label for="ClienteSt">

    nome <span class="required">*</span>

</label>

Solução

após uma breve busca no Google encontrei esse cara aqui:

http://kamranicus.com/Blog/Posts/3/a-better-mvc-label-helper

ele possui uma extensão quase perfeita para essa situação, onde será necessário utilizar @Html.FieldLabelFor(m => m.ClienteSt)

lembrando que é necessário adicionar sua classe com @using YourProject.Library.Extensions; no inicios do .cshtml ou adicionar no web.config (referência: http://stackoverflow.com/a/11897078 )

 

Obrigatório, não Opcional

só não é perfeita para essa situação porque ele adiciona “(optional)” nos campos não obrigatórios e no meu caso preciso de “*” nos campos obrigatórios, então fiz uma pequena alteração no código dele (linha 43)

if (metadata.IsRequired)
{
        htmlBuilder.Append(" <span class='required'>*</span>");
}

 

Atributos HTML customizados

isso resolve o problema, mas ainda sim tem uma situação que seria interessante no código dele que não foi adicionado, que seria a questão do class do CSS customizado, entre outros atributos HTML personalizados que poderiam ser adicionados através do helper, então fiz a seguinte alteração:

– adicionei o parâmetro object htmlAttributes no método e alterei a partir da linha 38 para:

StringBuilder htmlBuilder = new StringBuilder();

var attributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
TagBuilder tag = new TagBuilder("label");
tag.Attributes.Add("for", html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(propName));
tag.MergeAttributes(attributes, false);

assim meu helper agora vai me pemitir  @Html.FieldLabelFor(c => c.ClienteSt, new { @class = “css-customizado-1 css-customizado-2” })

 

Código Final

espero que tenha conseguido explicar bem os códigos e as situações, abaixo segue o código final da minha extensão:

public static class MvcHtmlExtensions
{

        public static MvcHtmlString FieldLabelFor<TModel, TResult>(this HtmlHelper<TModel> html, Expression<Func<TModel, TResult>> expression, object htmlAttributes, string labelText)
        {
            string propName = ExpressionHelper.GetExpressionText(expression);
            string unqualifiedPropName = propName.Split('.').Last(); // if there is a . in the name, take the rightmost part.

            ModelMetadata metadata = html.ViewData.ModelMetadata.Properties.First(p => p.PropertyName == propName);

            string finallabelText = labelText ?? metadata.DisplayName ?? metadata.PropertyName ?? unqualifiedPropName;
            if (String.IsNullOrEmpty(finallabelText))
            {
                return MvcHtmlString.Empty;
            }

            StringBuilder htmlBuilder = new StringBuilder();

            var attributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
            TagBuilder tag = new TagBuilder("label");
            tag.Attributes.Add("for", html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(propName));
            tag.MergeAttributes(attributes, false);

            htmlBuilder.Append(finallabelText);

            if (metadata.IsRequired)
            {
                htmlBuilder.Append(" <span class='required'>*</span>");
            }

            // Check description in metadatatype too
            var metaDataType = metadata.ContainerType.GetCustomAttributes(typeof(MetadataTypeAttribute), true).FirstOrDefault();
            var description = metadata.ContainerType.GetProperty(unqualifiedPropName)
            .GetCustomAttributes(typeof(DescriptionAttribute), true).FirstOrDefault();

            // No description attr on model prop, check metadatatype
            if (description == null && metaDataType != null)
            {
                var metaProp = ((MetadataTypeAttribute)metaDataType)
                .MetadataClassType.GetProperty(unqualifiedPropName);

                if (metaProp != null)
                    description = metaProp.GetCustomAttributes(typeof(DescriptionAttribute), true).FirstOrDefault();
            }

            if (description != null)
            {
                htmlBuilder.Append(String.Format("<span class=\"note\">{0}</span>", (description as DescriptionAttribute).Description));
            }

            tag.InnerHtml = htmlBuilder.ToString();

            return MvcHtmlString.Create(tag.ToString(TagRenderMode.Normal));
        }

        public static MvcHtmlString FieldLabelFor<TModel, TResult>(this HtmlHelper<TModel> html, Expression<Func<TModel, TResult>> expression, object htmlAttributes)
        {
            return FieldLabelFor(html, expression, htmlAttributes, null);
        }

        public static MvcHtmlString FieldLabelFor<TModel, TResult>(this HtmlHelper<TModel> html, Expression<Func<TModel, TResult>> expression)
        {
            return FieldLabelFor(html, expression, null, null);
        }

}

 

o resultado para @Html.FieldLabelFor(c => c.ClienteSt, new { @class = “col-lg-2 control-label” }) foi esse:

Resultado visual do HTML com FieldLabelFor

 

Enfim

extender o MVC é bem simples e bacaninha, espero que esse post seja muito helps para alguém

qualquer coisa postem nos comentários ou me procurem no twitter @JapaDavid

 

Referências

http://kamranicus.com/Blog/Posts/3/a-better-mvc-label-helper

http://stackoverflow.com/a/11897078

 

«