If I am passing HtmlAttributes into a template, like this:
#Html.DisplayFor(m => m.FirstName, new { htmlAttributes = new { #class = "orangetxt strongtxt" } })
In my template, how would I inject these into my HTML:
<span #ViewData["htmlAttributes"]>#Model</span>
This almost works, but it does some pretty weird stuff, so I'm assuming this isn't the way to go.
I realize I can accomplish this with an HtmlHelper extension method to render the full HTML element (span, in this case) and pass in the attributes that way, but is there a way to just render attributes straight into an HTML element, like the above example?
The below extension method will allow me to convert HtmlAttributes to a string:
public static MvcHtmlString RenderHtmlAttributes<TModel>(
this HtmlHelper<TModel> htmlHelper, object htmlAttributes)
{
var attrbituesDictionary = new RouteValueDictionary(htmlAttributes);
return MvcHtmlString.Create(String.Join(" ",
attrbituesDictionary.Select(
item => String.Format("{0}=\"{1}\"", item.Key,
htmlHelper.Encode(item.Value)))));
}
Then, to render them within the tag, I can just do this:
<span #Html.RenderHtmlAttributes(ViewData["htmlAttributes"])>#Model</span>
Jerad Rose's answer is good, but I ran into couple of issues with it:
It does not not convert underscores to dashes in attribute names
It does not handle no-value attributes gracefully
To address first issue, use HtmlHelper.AnonymousObjectToHtmlAttributes.
Below is my modification of Jerad's method:
public static MvcHtmlString RenderHtmlAttributes(this HtmlHelper helper, object htmlAttributes)
{
if (htmlAttributes == null) return new MvcHtmlString(String.Empty);
var attrbituesDictionary = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
return new MvcHtmlString(String.Join(" ", attrbituesDictionary.Select(item => string.IsNullOrEmpty((string)item.Value) ? String.Format("{0}", item.Key) : String.Format("{0}=\"{1}\"", item.Key, helper.Encode(item.Value)))));
}
Try this instead,
#Html.DisplayFor(m => m.FirstName,
new { htmlAttributes = "class = orangetxt strongtxt"})
This will render a string, whereas your version did do weird stuff, rendered { } as part of the output.
DisplayFor() is used to render the template that matches the property type.
Display templates are .cshtml files inside /DisplayTemplates folder which in turn is inside a view folder (i.e. any folder from Home, Shared or even a specific controller).
An example.
If you've a String.cshtml template like this inside /Views/Shared:
#model String
#if (string.IsNullOrEmpty(Model)) {
<span>(no string)</span>
}
else {
<span>#Model</span>
}
Every time you call DisplayFor() for a string property:
DisplayFor(model => model.MyStringProperty);
It renders the template accordingly to the string's value. You can be more specific and put /DisplayTemplates inside a specific View folder and them only calls from those views are affected by the template.
In your case you can be even more specific and call DisplayFor() with a particular template.
Suppose you've a template for a particular property, called MyPropertyTemplate.cshtml. You would call DisplayFor() like this:
DisplayFor(model => model.MyProperty, "MyPropertyTemplate");
And them, inside that template you can have whatever HTML attributes you want.
#model MyProperty
<span class="orangetxt strongtxt">#MyProperty.ToString()</span>
PS: When it doesn't find a template I guess it only calls model.Property.ToString() without additional html.
FYI: EditorFor(), for example, works in a similar way but it uses /EditorTemplates folder.
Related
I created an extension method based on this answer to the SO question c# - How can I create a Html Helper like Html.BeginForm - Stack Overflow and it works fine.
Can I move the embedded HTML in the extension method into a partial view and use that partial view in the method while preserving it's current behavior? In particular, I want to be able to 'wrap' a block of arbitrary HTML.
I ask not out of any pressing need, but simply out of a desire to maintain HTML consistently, e.g. as views and partial views. I imagine it will be a lot easier to spot any problems with the HTML if it's in a view or partial view too.
Here's the HtmlHelper extension method:
public static IDisposable HelpTextMessage(this HtmlHelper helper, bool isHidden, string heading)
{
TextWriter writer = helper.ViewContext.Writer;
writer.WriteLine(
String.Format(
"<div class=\"help-text {0}\">",
isHidden ? "help-text-hidden" : ""));
writer.WriteLine(
String.Format(
"<div class=\"help-text-heading\">{0}</div>",
heading));
writer.Write("<div class=\"help-text-body\">");
return new HelpTextMessageContainer(writer);
}
Here's the HelpTextMessageContainer class:
private class HelpTextMessageContainer : IDisposable
{
private readonly TextWriter _writer;
public HelpTextMessageContainer(TextWriter writer)
{
_writer = writer;
}
public void Dispose()
{
_writer.Write("</div></div>");
}
}
In a view, I can use the extension method like this:
#using(Html.HelpTextMessage(Model.HelpText.IsHelpTextHidden(Model.SomeHelpMessage), "Something"))
{
#:To do something, first do something-more-specific, then do another-something-more-specific.
}
Or I could use it like this too:
#using(Html.HelpTextMessage(Model.HelpText.IsHelpTextHidden(Model.SomeHelpMessage), "Something"))
{
<p>To do something, first do something-more-specific, then do another-something-more-specific.</p>
<p>Also, keep in mind that you might need to do something-else-entirely if blah-blah-blah.</p>
}
I haven't found any way to move the "embedded HTML" into a partial view exactly, but a slightly more-friendly way to encapsulate the HTML in a way that provides HTML and Razor syntax highlighting and Intellisense is to move into a view helper function in a file under the app App_Code folder as described in this post on "Hugo Bonacci's Blog".
Here's my helper function:
#helper HelpTextMessage(bool isHidden, string heading, Func<object, object> content)
{
<div class="help-text #(isHidden ? "help-text-hidden" : "")">
<div class="help-text-heading">
#heading
</div>
<div class="help-text-body">
#content(null)
</div>
</div>
}
Assuming the above helper function is in a file named ViewHelpers.cshtml in the App_Code folder, it can be called in a view like this:
#ViewHelpers.HelpTextMessage(false, "Something",
#:To do something, first do something-more-specific, then do another-something-more-specific.
)
or this:
#ViewHelpers.HelpTextMessage(false, "Something",
<p>To do something, first do something-more-specific, then do another-something-more-specific.</p>
<p>Also, keep in mind that you might need to do something-else-entirely if blah-blah-blah.</p>
)
I like having the embedded HTML in a view more than I do being able to use the #using(Html.HelpTextMessage(...){ ... } syntax, so I'm pretty happy with this as a solution.
I have a property of type "float" in my ViewModel. It's being displayed as a TextBox with a default value of "0".
I added an "EditorTemplates" folder inside the "Shared" folder & created a new "Float.cshtml" file with the following content:
#Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue == 0 ? "" : ViewData.TemplateInfo.FormattedModelValue, new { #class = "text-box single-line" })
However, still when I run the application, float fields are still being displayed with a default value of 0.
Thanks
UPDATE
I am just trying to see how ASP.NET reacts to custom templates, till now, the engine is not processing my custom template, something similar to:
LatLng.cshtml
#model float
#Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue, new { #class = "text-box single-line "}) Latitude
On the ViewModel,
[UIHint("LatLng")]
public float? Latitude { get; set; }
On the View,
#Html.EditorFor(model => model.Latitude)
Nothing is changing, default template is being used.
Float is not actually a .NET type, it's a C# type. Float maps to System.Single, so you need to create a Single.cshtml and not a Float.cshtml.
You can also get around this by specifying a UIHint attribute on the model data, or by specifying the template to use in your Editor or EditorFor methods.
An easy workaround is if you just set ViewData.TemplateInfo.FormattedModelValue to return a string in the model, so you don't have to do that weird logic on the view. If you need it to post back a new value (for editing purposes), you just have to add some logic in the controller to turn the string back into a float.
Embarrassingly newbie question:
I have a string field in my model that contains line breaks.
#Html.DisplayFor(x => x.MultiLineText)
does not display the line breaks.
Obviously I could do some fiddling in the model and create another field that replaces \n with <br/>, but that seems kludgy. What's the textbook way to make this work?
A HtmlHelper extension method to display string values with line breaks:
public static MvcHtmlString DisplayWithBreaksFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression)
{
var metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
var model = html.Encode(metadata.Model).Replace("\r\n", "<br />\r\n");
if (String.IsNullOrEmpty(model))
return MvcHtmlString.Empty;
return MvcHtmlString.Create(model);
}
And then you can use the following syntax:
#Html.DisplayWithBreaksFor(m => m.MultiLineField)
i recommend formatting the output with css instead of using cpu consuming server side strings manipulation like .replace,
just add this style property to render multiline texts :
.multiline
{
white-space: pre-wrap;
}
then
<div class="multiline">
my
multiline
text
</div>
newlines will render like br elements, test it here https://refork.codicode.com/xaf4
In your view, you can try something like
#Html.Raw(Html.Encode(Model.MultiLineText).Replace("\n", "<br />"))
The display template is probably the best solution but there is another easy option of using an html helper if you know you're just displaying a string, e.g.:
namespace Shaul.Web.Helpers
{
public static class HtmlHelpers
{
public static IHtmlString ReplaceBreaks(this HtmlHelper helper, string str)
{
return MvcHtmlString.Create(str.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.None).Aggregate((a, b) => a + "<br />" + b));
}
}
}
And then you'd use it like:
#using Shaul.Web.Helpers
#Html.ReplaceBreaks(Model.MultiLineText)
You create a display template for your data.
Here's a post detailing how to do it.
How do I create a MVC Razor template for DisplayFor()
In that template you do the actual translating of newlines into and whatever other work needs to be done for presentation.
Inspired by DisplayTemplates for common DataTypes,
I override (introduce?) a default DisplayTemplate for DataType.MultilineText, /Views/Shared/DisplayTemplates/MultilineText.cshtml containing just this line:
<span style="white-space: pre-wrap">#this.Model</span>
(Of course you could replace this style, by a css-class, or replace newlines inside the view, if you prefer that.)
I guess this template is automatically resolved, because I had no need for UIHint or any other reference or registration.
Using the DisplayTemplate instead of introducing a HtmlHelper-method has the advantage, that it trickles down to properties and views that are not explicitly defined.
E.g. DisplayFor(MyClassWithMultilineProperties) will now also correctly display MyClassWithMultilineProperties.MyMultilineTextProperty, if the property was annotated with [DataType(DataType.MultilineText)].
Try using
#Html.Raw("<p>" + Html.LabelFor(x => x.Name) + "</p>")
Here's another extension method option.
public static IHtmlString DisplayFormattedFor<TModel>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, string>> expression)
{
string value = Convert.ToString(ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData).Model);
if (string.IsNullOrWhiteSpace(value))
{
return MvcHtmlString.Empty;
}
value = string.Join("<br/>", value.Split(new[] { Environment.NewLine }, StringSplitOptions.None).Select(HttpUtility.HtmlEncode));
return new HtmlString(value);
}
I had this problem with ASP.NET Core 6. The previous answers here did not work with a linq expression in Html.DisplayFor. Instead I was constantly getting the <br/> tag escaped out in the output HTML. Trying HtmlString helper methods suggestions did not work.
The following solution was discovered through trial and error. The InfoString had CRLF replaced with the <br/> tags as shown in the property code.
Works
#Html.Raw(#Convert.ToString(item.InfoString))
Did not work
#Html.DisplayFor(modelItem => item.InfoString)
FYI - my Info String property:
public string InfoString
{
get { return MyInfo.Replace(Environment.NewLine,"<br />"); }
}
I'm fairly new to MVC 3 and am using the Razor view engine. I'm using the Html.Hidden extension method to output input elements of type hidden. What I woudl also like to do is add a custom attribute to hold a dynamic value. I was under the impression in HTML5 wee could write custom html element attributes that are prefixed with 'data-'. I'm trying to do something like below;
#Html.Hidden("hdnID", mymodel.somevalue, new { data-uniqueid = mymodel.somevalue })
hoping to render;
<input type="hidden" value="mymodel.somevalue" data-uniqueid="mymodel.somevalue"/>
The htmlAttributes part (new { data-uniqueid = mymodel.somevalue }) is giving the error,
"Invalid anonymous type member declarator. Anonymous type members must be declared with a member assignment, simple name or member access".
Can I add user-defined attribute to html elements using the HtmlHelper classes?
Regards,
Use:
#Html.Hidden("hdnID", mymodel.somevalue, new { #data_uniqueid = mymodel.somevalue })
The underscore gets automatically converted to a dash.
Doh! I was being silly. You can't have '-' in the anon type declaration:
data-uniqueid = ...it must be
datauniqueid = ....
In that case, your best best is to write out the hidden input by hand:
<input type="hidden" value="#mymodel.somevalue" data-uniqueid="#mymodel.somevalue"/>
You can step around the member validation by constructing a dictionary object. As follows:
#Html.TextBoxFor(model => model.Phone, new Dictionary<string, object>
{
{
"data-call-results-target", "#search-results-area"
},
{
"data-action-path", "/Controler/Method"
}
})
I'm creating a custom helper to automate some code in my application. I'd like now how display a control in my helper. When I return the GetHTML() method, the page display the HTML like a plain text. When I use the Render() method the control is rendere in body, out of order.
public static string EntityForm(this HtmlHelper helper, Type TypeModel)
{
return "My Helper" + DevExpress.Web.Mvc.UI.ExtensionsFactory.Instance.TextBox(settings =>
{
settings.Name = att.Nome;
}).GetHtml()
}
Use HtmlString, this way it does not encode the output.
Example from inside a view
#(new HtmlString("<div>some html</div>"))
Changing your Html Helper
Try changing your method to the following:
public static HtmlString EntityForm(this HtmlHelper helper, Type TypeModel)
{
var html = "My Helper" + DevExpress.Web.Mvc.UI.ExtensionsFactory.Instance.TextBox(settings =>
{
settings.Name = att.Nome;
}).GetHtml();
return new HtmlString(html);
}
Razor will escape all strings written to the page.
You need to change your helper method to return an HtmlString so that Razor won't escape it.