Link to home
Start Free TrialLog in
Avatar of solution1368
solution1368

asked on

mvc, razor, html helper

I am using MVC 3 with razor with the following codes.

 @Html.LabelFor(m => m.email, new { @class="control-label" })


 I just want to the razor codes above convert into below, and I got the following error:

 <label class="control-label" for="inputRegisterFirstName">Email</label>

 
alert

 Compiler Error Message: CS1928: 'System.Web.Mvc.HtmlHelper<SuretyNetwork.Controllers.SignUpModel>' does not contain a definition for 'LabelFor' and the best extension method overload 'System.Web.Mvc.Html.LabelExtensions.LabelFor<TModel,TValue>(System.Web.Mvc.HtmlHelper<TModel>, System.Linq.Expressions.Expression<System.Func<TModel,TValue>>, string)' has some invalid arguments

How to fix it?
Avatar of Stephan
Stephan
Flag of Netherlands image

That is strange, it should work, I tried the following and get no errors:

@model string
@Html.LabelFor(x => x, new { @class = "option" })

Open in new window


Do you have the following lines in your web.config inside the "views" folder?

<add namespace="System.Web.Mvc" />
        <add namespace="System.Web.Mvc.Ajax" />
        <add namespace="System.Web.Mvc.Html" />

Open in new window

Avatar of solution1368
solution1368

ASKER

What mvc ver you use?
I am using mvc4, but it should be for version 3 and 4 thesame
i use mvc 3 but it is not working yes. it is there under view folder
Sorry for my late response..

I did some research and the overload you want to use does not exist in MVC 3.
You can create your own extension to support this behaviour.

Here is the code you need:

public static class LabelExtensions
{
    public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, object htmlAttributes)
    {
        return LabelFor(html, expression, new RouteValueDictionary(htmlAttributes));
    }
    public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, IDictionary<string, object> htmlAttributes)
    {
        ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
        string htmlFieldName = ExpressionHelper.GetExpressionText(expression);
        string labelText = metadata.DisplayName ?? metadata.PropertyName ?? htmlFieldName.Split('.').Last();
        if (String.IsNullOrEmpty(labelText))
        {
            return MvcHtmlString.Empty;
        }

        TagBuilder tag = new TagBuilder("label");
        tag.MergeAttributes(htmlAttributes);
        tag.Attributes.Add("for", html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(htmlFieldName));

        TagBuilder span = new TagBuilder("span");
        span.SetInnerText(labelText);

        // assign <span> to <label> inner html
        tag.InnerHtml = span.ToString(TagRenderMode.Normal);

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

Open in new window

thank. where the codes should be located? if i want all the cshtml be able use it.
It does not matter. Just make sure you have set the namespace in the web.config inside the views folder
show me in codes. I tried it and no working.
Add this class:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Web.Mvc;
using System.Web.Routing;

namespace MvcApplication1.Helpers
{
    public static class LabelExtensions
    {
        public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, object htmlAttributes)
        {
            return LabelFor(html, expression, new RouteValueDictionary(htmlAttributes));
        }
        public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, IDictionary<string, object> htmlAttributes)
        {
            ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
            string htmlFieldName = ExpressionHelper.GetExpressionText(expression);
            string labelText = metadata.DisplayName ?? metadata.PropertyName ?? htmlFieldName.Split('.').Last();
            if (String.IsNullOrEmpty(labelText))
            {
                return MvcHtmlString.Empty;
            }

            TagBuilder tag = new TagBuilder("label");
            tag.MergeAttributes(htmlAttributes);
            tag.Attributes.Add("for", html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(htmlFieldName));

            TagBuilder span = new TagBuilder("span");
            span.SetInnerText(labelText);

            // assign <span> to <label> inner html
            tag.InnerHtml = span.ToString(TagRenderMode.Normal);

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

Open in new window


And place the following line in your web.config file within the views folder.

<add namespace="MvcApplication1.Helpers"/>

Open in new window


Then if you have the view already opened it, close it. and then open it agian to reload the intellisense.

Now you should be able to do the following (example on a empty view):
@model string
@Html.LabelFor(x => x, new { @class = "control-label" } )

Open in new window

great working.
one more question.

If I want <span class="form-required" title="This field is required.">*</span> INSIDE
of the label like below. What should I do?

 <label class="control-label" for="inputIndemnitorSSN">
                            SSN
                            <span class="form-required" title="This field is required.">*</span>
                        </label>
You can do this in 2 ways, using the metadata from the property or create a custom method.

This is a way to do the metadata:

public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, IDictionary<string, object> htmlAttributes)
        {
            ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
            string htmlFieldName = ExpressionHelper.GetExpressionText(expression);
            string labelText = metadata.DisplayName ?? metadata.PropertyName ?? htmlFieldName.Split('.').Last();
            if (String.IsNullOrEmpty(labelText))
            {
                return MvcHtmlString.Empty;
            }

            TagBuilder tag = new TagBuilder("label");
            tag.MergeAttributes(htmlAttributes);
            tag.Attributes.Add("for", html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(htmlFieldName));
            tag.SetInnerText(labelText);

            if (metadata.IsRequired)
            {
                TagBuilder required = new TagBuilder("span");
                required.Attributes.Add("class", "form-required");
                required.Attributes.Add("title", "This field is required.");
                required.InnerHtml = "*";
                tag.InnerHtml += required.ToString(TagRenderMode.Normal);
            }

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

Open in new window


And I think you can manage yourself how to overload this with a custom method.
I still don't get it. Should I just copy and paste your new codes to achieve both requests I have? Actually, I did copy and paste and replace the codes and it is not working.
The class I gave you at first  (comment #a39551448) can be updated to:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Web.Mvc;
using System.Web.Routing;

namespace MvcApplication1.Helpers
{
    public static class LabelExtensions
    {
        public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, object htmlAttributes)
        {
            return LabelFor(html, expression, new RouteValueDictionary(htmlAttributes));
        }
        public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, IDictionary<string, object> htmlAttributes)
        {
            ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
            string htmlFieldName = ExpressionHelper.GetExpressionText(expression);
            string labelText = metadata.DisplayName ?? metadata.PropertyName ?? htmlFieldName.Split('.').Last();
            if (String.IsNullOrEmpty(labelText))
            {
                return MvcHtmlString.Empty;
            }

            TagBuilder tag = new TagBuilder("label");
            tag.MergeAttributes(htmlAttributes);
            tag.Attributes.Add("for", html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(htmlFieldName));
            tag.SetInnerText(labelText);

            if (metadata.IsRequired)
            {
                TagBuilder required = new TagBuilder("span");
                required.Attributes.Add("class", "form-required");
                required.Attributes.Add("title", "This field is required.");
                required.InnerHtml = "*";
                tag.InnerHtml += required.ToString(TagRenderMode.Normal);
            }

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

Open in new window


Then, if you set RequiredAttribute to the property (e.g. email)

[Required]
public string Email { get; set; }

Open in new window


It should show because I tested it myself.
First of all, thank you for your time to help me. But I think I should get you more information what I really need to do.

<label class="control-label" for="inputIndemnitorSSN">
                            SSN
                            <span class="form-required" title="This field is required.">*</span>
                        </label>

The goal is to add <span></span> inside of the label. And since not all of the <label> will contain <span></span>. So I assume something like the following will work to me.

@Html.LabelFor(x => x, new { @class = "control-label" } , AddSpan) for example if I want add.
@Html.LabelFor(x => x, new { @class = "control-label" } ) for example If I don't want to add.

The codes may be wrong, but I just try to explain what I need to do. Is it a way to modify in the labelExtensions Class?

Again, thank you for your helps. I am very new to MVC especially to the custom extensions side.
No worry about what I posted. I finally get it okay but now have another issue.
I actually have separate class library. and I add labelExtensions there and
then go back to MVC website to add reference. Then, I added namespace into the web.config

Now it alerts the class is not namespace type. :-(
Not sure what is going on here.

So in steps you did the following:
- Moved the labelextensions class to a different class library (e.g. MyCompany.Extensions)
  - So the namespace is MyCompany.Extensions
- Referenced the class library to the web project (MyCompany.Web
- Added the namespace of the labelextensions class in the /Views/web.config file.

Did I miss something?
your procedure list looks right to what I tried to do.
But it is still not working after I done all the steps
And now it creates one more issue.

when it is validated, and show 'required' it end up create a new line. even i tried to add float:left in css. It does not resolve the issue.
Ok, what is the status now?

Now it alerts the class is not namespace type. :-(
Is this fixed?

when it is validated, and show 'required' it end up create a new line. even i tried to add float:left in css. It does not resolve the issue.
What else did you place (in this question) besides the label?
Both items are still not fixed for your status.
Now I just put the label extensions cs back to the mvc project and it works and I take out the required in the model

But both must be fixed thank
ASKER CERTIFIED SOLUTION
Avatar of Stephan
Stephan
Flag of Netherlands image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial