Brandon S
Brandon S

Reputation: 33

MVC 3 Html.DisplayFor(...) code not generating HTML code

I'm working on a generic Details page in MVC 3 that looks like this in the View:

<fieldset>
    <legend style="font-size:large">Info</legend>

    <div class="display-label">Url1:</div>
    <div class="display-field">
        @Html.DisplayFor(model => model.Url1)
    </div>

    <div class="display-label">Url2:</div>
    <div class="display-field">
        @Html.DisplayFor(model => model.Url2)
    </div>

<fieldset>

and like this in the Model:

public HtmlString Url1
{
    get
    {
        if (!string.IsNullOrEmpty(Url1))
        {
            return new HtmlString("<a href=\"" + Url1 
                                  + "\" target=\"_blank\">" + Url1 + "</a>");
        }
        return new HtmlString("<b>no url</b>");
    }
}

public HtmlString Url2
{
    get
    {
        if (!string.IsNullOrEmpty(Url2))
        {
            return new HtmlString("<a href=\"" + Url2
                                  + "\" target=\"_blank\">" + Url2 + "</a>");
        }
        return new HtmlString("<b>no url</b>");
    }
}

With Url1 being a url with the form http://www.website.com, and Url2 having the form www.website.org/file.aspx

When I debug my code the HtmlString for Url1 displays correctly, but the HtmlString for Url2 does not. The resulting Html source looks like:

<div class="display-label">Url1</div>
    <div class="display-field">
        <a href="http://www.website.org">website</a> <br/>
    </div>

<div class="display-label">Url2</div>
    <div class="display-field">

    </div>

Has anyone experienced issues like this before, and if so how did you fix them?

Update

Updated code to use @Model.Url1 instead of @Html.DisplayFor(Url1). Problem solved.

Upvotes: 1

Views: 1276

Answers (2)

Brad Christie
Brad Christie

Reputation: 101604

The DisplayFor helper is there to take advantage of DisplayTemplates for models (and their properties). Given you're populating HTML into these properties, you simply want to directly output them. However, because MVC encodes all output, you'll also need to introduce the @Html.Raw() helper. So, something like the following:

@Html.Raw(Model.Url1)

However, this really isn't good practice for MVC in terms of model usage. I would recommend (if you must use HTML) using the IHtmlString interface for your property type (so now it's output directly using @Model.Url1) or making a custom object and display template (great use case for UIHintAttribute).


IHtmlString Example:

class UrlViewModel : IHtmlString {
  private readonly String url;
  private UrlViewModel(String url) {
    this.url = url;
  }

  public String ToHtmlString(){
    if (String.IsNullOrEmpty(this.url)){
      return new HtmlString("<a ...>...</a>");
    }
    return new HtmlString("<b>...</b>");
  }
}

Your model then becomes:

class MyViewModel {
  private String url1;
  private String url2;
  public MyViewModel(String url1, String url2){
    this.ur1l = url1;
    this.url2 = url2;
  }

  public UrlViewModel Url1 { get; set; }
  public UrlViewModel Url2 { get; set; }
}

And finally your view:

@Html.Url1 @* MVC automatically recognized an IHtmlString *@
@Html.Url2 @* implementation and outputs without escaping *@

UIHint Usage

Your model gets decorated (but retains type):

class MyViewModel {
  [UIHint("MyUrl")]
  public String Url1 { get; set; }
  [UIHint("MyUrl")]
  public String Url2 { get; set; }
}

Also, simply provide the URL (and no HTML) to the model. Then, add a new DisplayTemplate (~/Views/Shared/DisplayTemplates/MyUrl.cshtml):

@model String
@if (!String.IsNullOrEmpoty(Model)) {
  <a href="@Model" target="_blank">@Model</a>
} else{
  <b>No Url</b>
}

And you can continue to use @Html.DisplayFor(x => x.Url1)

Upvotes: 2

Paul Abbott
Paul Abbott

Reputation: 7211

DavidG is right, you are putting UI code in your model, which is not a good thing.

Instead, you should keep your URLs as strings and use the UIHintAttribute to tell MVC which partial you want to use to display the data.

[UIHint("MyCustomHyperlink")]
public string URL1 { get; set; }

[UIHint("MyCustomHyperlink")]
public string URL2 { get; set; }

Then create a partial called MyCustomHyperlink.cshtml in /Views/Shared/DisplayTemlpates

@model string
@if (!string.IsNullOrEmpty(model)) {
    <a href="@model" target=\"_blank\">@model</a>                  
}
else {
    <b>no url</b>
}

Upvotes: 2

Related Questions