Using an HtmlHelper inside a custom HtmlHelper - asp.net-mvc

Is there a way to do something like the following:
public static IHtmlString TableFor(this HtmlHelper helper, IEnumerable<MaterialGroup> groups, Func<HtmlHelper, MaterialGroup, int, string> tableContentsFunc)
{
return MvcHtmlString.Create("#Html.TextBoxFor(x => this.Model.Something)");
}
Obviously this is a trivial example, but when ever I try something of the sort it renders the Helpers i.e. "#Html.TextBoxFor(x => this.Model.Something)" as text on the page instead of processing them as helpers.
Is there a way to achieve this?
public static MvcHtmlString TableFor(this HtmlHelper<IEnumerable<MaterialGroup>> helper, IEnumerable<MaterialGroup> groups, Func<HtmlHelper<MaterialGroup>, MaterialGroup, int, string> tableContentsFunc)
{
String html = "<table class='materials joists'>";
String endHtml = "</table>";
for (int i = 0; i < groups.Count(); ++i)
{
HtmlHelper<MaterialGroup> groupHelper = new HtmlHelper<MaterialGroup>(helper.ViewContext, helper.ViewDataContainer); // Crashes here with cannot convert IEnumerable<MaterialGroup> to MaterialGroup.
html += TbodyFor(groupHelper , groups.ElementAt(i), i);
html += tableContentsFunc(groupHelper , groups.ElementAt(i), i);
}
return MvcHtmlString.Create(html + endHtml);
}
public static string TbodyForJoists(this HtmlHelper<MaterialGroup> helper, MaterialGroup group, int index)
{
string html = string.Empty;
MvcHtmlString markTextbox = InputExtensions.TextBoxFor<MaterialGroup, String>(helper, x => group.Joists.ElementAt(i).Mark, new { Name = "MaterialGroups[" + index + "].Joists[" + i + "].Mark", Class = "auto-size first-column" });
html += martTextbox;
.
.
.
return html;
}
When I attempt the above I get issues with the HtmlHelper<> Types.
If I leave it with just HtmlHelper I get an error telling me to explicity state since it doesn't know what I doing with it. If I explicitly state is have conversion? issues I guess you could say.
How can I simply just use the TextBoxFor in this way?

Because that is very literally what you are telling it to do. The output of the helper itself is not processed by Razor. Whatever you return is what's going on the page.
However, you could always do:
MvcHtmlString textBox = Html.TextBoxFor(expression);
And, you'd have to feed the helper the expression to use. It perhaps would be easier in this situation to use Html.TextBox instead, but then you're going to have to do more work to try to figure out the right names and such for the fields.
It's going to be far easier and less convoluted to just use editor templates for this type of thing.

Related

How to properly pass string to Razor?

I want to create a simple function in a static class ChardinHtml.DataIntro(string message). The function is supposed to render something like data-intro='my message' and I want to use it that way:
<div #ChardinHtml.DataIntro("These are your site's settings")/>.
(The output would be <div data-intro="These are your site's settings"/> )
What exactly should I return?
Is it string/encoded string/MvcHtmlString/MvcHtmlString with encoded string inside? What should I do to protect myself from characters like ( ' ) (apostrophe) inside a message?
The code looks like this:
public static string DataIntro(string msg)
{
string str = string.Format("data-intro='{0}'", msg);
return str;
}
You can create an HTML helper method to render this. You might return MvcHtmlString as the output. Use HttpUtility.HtmlEncode to encode the string before you use it
public static class MyCustomFancyHtmlExtensions
{
public static MvcHtmlString MyFancyAttr(this HtmlHelper helper, string msg)
{
msg = HttpUtility.HtmlEncode(msg);
return new MvcHtmlString(string.Format("data-intro ='{0}'", msg));
}
}
And you can call it like after including the namespace in the razor view using the using statement
#usint YourNamespaceWhereYouDefinedTheMethod
<div class="test" #Html.MyFancyAttr("test'ing")> test</div>
While this answer your question, I am not sure what your use case is, But my recommendation is to write the data attribute directly in the razor view unless you have some complex logic involved in determining what/when to render this.

Creating a htmlhelper method to remove quotes in strings

I want to create a htmlhelper method in an MVC4 project using Entity Framework 5.0
For one of my pages, descriptions are loaded. Most descriptions are:
description description description
but some are
"description description description"
Now how do I write a htmlhelper method that removes these " " quotes?
Below is an example of an already working htmlhelper method that adds three dots for descriptions that are too long:
public static class HtmlHelpers
{
public static string Truncate(this HtmlHelper helper, string input, int length)
{
if(input.Length <= length)
{
return input;
}
else
{
return input.Substring(0,length) + "...";
}
}
So I basically need something like above but with the purpose of not displaying " " quotes
You can use the String.Trim(Char[]) (MSDN) method to get rid of the leading and trailing double quotes in a string.
string foo = "\"This is a quoted string\"".Trim('"');
Then you don't really need an HTML helper you can just use Trim() directly in your view.
#Model.Description.Trim('"')
Or make it a property of your model :
public string DescriptionWithoutQuotes
{
get { return this.Description.Trim('"'); }
}
Using a HTML helper for this would be overkill, in my opinion.
Try this:
public static string Unquote(this HtmlHelper helper, string input)
{
if (string.IsNullOrWhiteSpace(input))
return string.Empty;
return input.Replace("\"", "");
}

Build html strings in HtmlHelper extension methods

Can someone be kind and explain why I should write strings in this way:
public static MvcHtmlString Render(this HtmlHelper htmlHelper, MenuItem menuItem)
{
if (!menuItem.IsVisible)
return MvcHtmlString.Empty;
var li = new TagBuilder("li");
var a = new TagBuilder("a");
a.MergeAttribute("href", menuItem.Uri);
a.SetInnerText(menuItem.Title);
li.InnerHtml = a.ToString();
return MvcHtmlString.Create(li.ToString());
}
When this is so much cleaner:
public static MvcHtmlString Render(this HtmlHelper htmlHelper, MenuItem item)
{
if (!item.IsVisible)
return MvcHtmlString.Empty;
var str = string.Format(#"<li>{1}</li>", item.Uri, item.Title);
return MvcHtmlString.Create(str.ToString());
}
What are the benefits?
There isn't a great reason. I would say speed, since TagBuilder is essentially a specialized StringBuilder, which is known to be faster than string.Format, but I'd need to see some benchmarks, since it seems like the whole HTML structure thing might slow things down.
One example of why you might want to use it is that it makes conditional logic easier. I think for example something like
var theClass = item.Title.Length > 5 ? " class=\"Long\"" : "";
var str = string.Format(#"<li><a href=""{0}""{1}>{2}</a></li>", item.Uri, theClass, item.Title);
is not very clear or clean, whereas
if (item.Title.Length > 5)
{
a.AddCssClass("Long");
}
// And then all that other stuff.
is a pretty nice solution.
TagBuilder just ensures your HTML is well formed. I tend to use tagbuilder for anything I would use multiple lines for in HTML like an span within a href within an div for the extra protection from typos.

Is there any way to combine the output of two IHtmlStrings into a single IHtmlString instance?

I'm writing an ASP.NET MVC Html Helper which basically takes 2 HTML Helpers that return IHtmlStrings and combines them together and also returns them as an IHtmlString like so:
//this doesn't work
public static IHtmlString CompositeHelper(this HtmlHelper helper, string data)
{
//GetOutput returns an IHtmlString
var output1 = new Component1(data).GetOutput();
var output2 = new Component2(data).GetOutput();
return output1 + output2
}
Now I know this isn't going to work because IHtmlString is an interface with an implementation that is a complex type, but if I go
return output1.ToHtmlString() + output2.ToHtmlString()
I just get a normal string which gets HtmlEncoded when I return it from my HtmlHelper.
So my question is, how can I take the output form two IHtmlStrings and combine them into a single IHtmlString?
Like this:
return new HtmlString(output1.ToString() + output2.ToString());

Replace line break characters with <br /> in ASP.NET MVC Razor view

I have a textarea control that accepts input. I am trying to later render that text to a view by simply using:
#Model.CommentText
This is properly encoding any values. However, I want to replace the line break characters with <br /> and I can't find a way to make sure that the new br tags don't get encoded. I have tried using HtmlString but haven't had any luck yet.
Use the CSS white-space property instead of opening yourself up to XSS vulnerabilities!
<span style="white-space: pre-line">#Model.CommentText</span>
Try the following:
#MvcHtmlString.Create(Model.CommentText.Replace(Environment.NewLine, "<br />"))
Update:
According to marcind's comment on this related question, the ASP.NET MVC team is looking to implement something similar to the <%: and <%= for the Razor view engine.
Update 2:
We can turn any question about HTML encoding into a discussion on harmful user inputs, but enough of that already exists.
Anyway, take care of potential harmful user input.
#MvcHtmlString.Create(Html.Encode(Model.CommentText).Replace(Environment.NewLine, "<br />"))
Update 3 (Asp.Net MVC 3):
#Html.Raw(Html.Encode(Model.CommentText).Replace("\n", "<br />"))
Split on newlines (environment agnostic) and print regularly -- no need to worry about encoding or xss:
#if (!string.IsNullOrWhiteSpace(text))
{
var lines = text.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines)
{
<p>#line</p>
}
}
(remove empty entries is optional)
Omar's third solution as an HTML Helper would be:
public static IHtmlString FormatNewLines(this HtmlHelper helper, string input)
{
return helper.Raw(helper.Encode(input).Replace("\n", "<br />"));
}
Applying the DRY principle to Omar's solution, here's an HTML Helper extension:
using System.Web.Mvc;
using System.Text.RegularExpressions;
namespace System.Web.Mvc.Html {
public static class MyHtmlHelpers {
public static MvcHtmlString EncodedReplace(this HtmlHelper helper, string input, string pattern, string replacement) {
return new MvcHtmlString(Regex.Replace(helper.Encode(input), pattern, replacement));
}
}
}
Usage (with improved regex):
#Html.EncodedReplace(Model.CommentText, "[\n\r]+", "<br />")
This also has the added benefit of putting less onus on the Razor View developer to ensure security from XSS vulnerabilities.
My concern with Jacob's solution is that rendering the line breaks with CSS breaks the HTML semantics.
I needed to break some text into paragraphs ("p" tags), so I created a simple helper using some of the recommendations in previous answers (thank you guys).
public static MvcHtmlString ToParagraphs(this HtmlHelper html, string value)
{
value = html.Encode(value).Replace("\r", String.Empty);
var arr = value.Split('\n').Where(a => a.Trim() != string.Empty);
var htmlStr = "<p>" + String.Join("</p><p>", arr) + "</p>";
return MvcHtmlString.Create(htmlStr);
}
Usage:
#Html.ToParagraphs(Model.Comments)
I prefer this method as it doesn't require manually emitting markup. I use this because I'm rendering Razor Pages to strings and sending them out via email, which is an environment where the white-space styling won't always work.
public static IHtmlContent RenderNewlines<TModel>(this IHtmlHelper<TModel> html, string content)
{
if (string.IsNullOrEmpty(content) || html is null)
{
return null;
}
TagBuilder brTag = new TagBuilder("br");
IHtmlContent br = brTag.RenderSelfClosingTag();
HtmlContentBuilder htmlContent = new HtmlContentBuilder();
// JAS: On the off chance a browser is using LF instead of CRLF we strip out CR before splitting on LF.
string lfContent = content.Replace("\r", string.Empty, StringComparison.InvariantCulture);
string[] lines = lfContent.Split('\n', StringSplitOptions.None);
foreach(string line in lines)
{
_ = htmlContent.Append(line);
_ = htmlContent.AppendHtml(br);
}
return htmlContent;
}

Resources