changing Html.Raw into string without html markup in a razor view - asp.net-mvc

Quite simple question. I have the following code
#Html.Raw(following.Description).ToString()
when this comes from database it has some markup in it (its a forum post but i want to show a snippet in the list without the markup
is there any way to remove this and replace this line or shall I just regex it from the controller?

Here is a utility class extension method that is able to strip tags from fragments without using Regex:
public static string StripTags(this string markup)
{
try
{
StringReader sr = new StringReader(markup);
XPathDocument doc;
using (XmlReader xr = XmlReader.Create(sr,
new XmlReaderSettings()
{
ConformanceLevel = ConformanceLevel.Fragment
// for multiple roots
}))
{
doc = new XPathDocument(xr);
}
return doc.CreateNavigator().Value; // .Value is similar to .InnerText of
// XmlDocument or JavaScript's innerText
}
catch
{
return string.Empty;
}
}

Related

If not closed then Auto close or auto format html tag in dynamic content in razor

below content has pulled from database
<div class="main"><div class="col1">content</div>
in above example main div has not closed in my database so I want to close it.
I have simply add in my razor view page
#Html.Raw(a.shortDesc)
but my page has disturbed. so please suggest me.
I would suggest using the HtmlAgilityPack (https://html-agility-pack.net/) to fix the HTML before you render it out using #Html.Raw in your view.
In the ShortDesc property of your ViewModel, you could do something like this:
private string _shortDesc;
public string ShortDesc
{
get
{
var doc = new HtmlDocument();
doc.LoadHtml(_shortDesc);
return doc.DocumentNode.OuterHtml;
}
set
{
this._shortDesc = value;
}
}
#{
var data = #a.shortDesc+"</div>";
}
#Html.Raw(#data)

What templating library can be used with Asp .NET MVC?

In my MVC 5 app I need to be able to dynamically construct a list of fully qualified external URL hyperlinks, alone with some additional data, which will come from the Model passed in. I figure - I will need to construct my anchor tags something like this:
{{linkDisplayName}}
with AngularJS this would be natural, but, I have no idea how this is done in MVC.
Is there a templating library that can be used for this?
1) Create a model to Hold the Links
public class LinkObject
{
public string Link { get; set; }
public string Description { get; set; }
}
2) In your Action you can use ViewBag, ViewData or even pass the list inside you Model. I will show you how to do using ViewBag
public ActionResult MyDynamicView()
{
//Other stuff and code here
ViewBag.LinkList = new List<LinkObject>()
{
new LinkObject{ Link ="http://mylink1.com", Description = "Link 1"},
new LinkObject{ Link ="http://mylink2.com", Description = "Link 2"},
new LinkObject{ Link ="http://mylink3.com", Description = "Link 3"}
};
return View(/*pass the model if you have one*/);
}
3) In the View, just use a loop:
#foreach (var item in (List<LinkObject>)ViewBag.LinkList)
{
#item.Description
}
Just create a manual one for that, no need to do it from a template. For example, in javascript
function groupAnchor(url,display){
var a = document.createElement("a");
a.href = url;
a.className = "list-group-item";
a.target = "_blank";
a.innerHTML = display;
return a;
}
And then use that function to modify your html structure
<div id="anchors"></div>
<script>
document.getElementById("anchors").appendChild(groupAnchor("http://google.com","Google"));
</script>
Your approach to modification will more than likely be more advanced than this, but it demonstrates the concept. If you need these values to come from server side then you could always iterate over a set using #foreach() and issue either the whole html or script calls there -- or, pass the set from the server in as json and then use that in a function which is set up to manage a list of anchors.
To expand on this, it is important to avoid sending html to the view from a razor iteration. The reason being that html constructed by razor will increase the size of the page load, and if this is done in a list it can be a significant increase.
In your action, construct the list of links and then serialize them so they can be passed to the view
public ActionResult ViewWithLinks()
{
var vm = new ViewModel();
vm.Links = Json(LinkSource.ToList()).Data;
//or for a very simple test for proof of concept
var Numbers = Json(Enumerable.Range(0,100).ToList()).Data;
ViewData["numbers"] = Numbers ;
return View(vm);
}
where all you need is an object to hold the links in your view model
public class ViewModel
{
public ICollection<Link> Links { get; set; }
}
public class Link
{
public string text { get; set; }
public string href { get; set; }
}
and then in your view you can consume this json object
var allLinks = #Html.Raw(Json.Encode(Model.Links));
var numbersList = #Html.Raw(Json.Encode(ViewData["linkTest"]));//simple example
Now you can return to the above function in order to place it on the page by working with the array of link objects.
var $holder = $("<div>");
for(var i = 0; i < allLinks.length; i++){
$holder.append(groupAnchor(allLinks[i].href,allLinks[i].text));
}
$("#linkArea").append($holder);
The benefit is that all of this javascript can be cached for your page. It is loaded once and is capable of handling large amounts of links without having to worry about sending excessive html to the client.

How to get erroneous field's ID's in validation summary, in front of validation messages

Is there a way to customize the validationSummary so that it can output anchor tags who's HREF is the name of the field that the validation message in teh summary is displaying for? This way, using jquery, i can add onclick events that focus the field when the anchor tag is clicked on the validation summary.
This is primarely for visually impaired people, so that when they have errors, the validation summary focuses, they tab to an error entry, the anchor tag with the field label focuses and the screen reader reads the anchor then the message, then they can click on the anchor to focus on the erroneous field.
First Name - Please enter your first name.
Thanks.
I don't think that there is any functionality within the framework for this so you would need to use a custom extension method. For example:
public static string AccessibleValidationSummary(this HtmlHelper htmlHelper, string message, IDictionary<string, object> htmlAttributes)
{
// Nothing to do if there aren't any errors
if (htmlHelper.ViewData.ModelState.IsValid)
{
return null;
}
string messageSpan;
if (!String.IsNullOrEmpty(message))
{
TagBuilder spanTag = new TagBuilder("span");
spanTag.MergeAttributes(htmlAttributes);
spanTag.MergeAttribute("class", HtmlHelper.ValidationSummaryCssClassName);
spanTag.SetInnerText(message);
messageSpan = spanTag.ToString(TagRenderMode.Normal) + Environment.NewLine;
}
else
{
messageSpan = null;
}
StringBuilder htmlSummary = new StringBuilder();
TagBuilder unorderedList = new TagBuilder("ul");
unorderedList.MergeAttributes(htmlAttributes);
unorderedList.MergeAttribute("class", HtmlHelper.ValidationSummaryCssClassName);
foreach (string key in htmlHelper.ViewData.ModelState.Keys)
{
ModelState modelState = htmlHelper.ViewData.ModelState[key];
foreach (ModelError modelError in modelState.Errors)
{
string errorText = htmlHelper.ValidationMessage(key);
if (!String.IsNullOrEmpty(errorText))
{
TagBuilder listItem = new TagBuilder("li");
TagBuilder aTag = new TagBuilder("a");
aTag.Attributes.Add("href", "#" + key);
aTag.InnerHtml = errorText;
listItem.InnerHtml = aTag.ToString(TagRenderMode.Normal);
htmlSummary.AppendLine(listItem.ToString(TagRenderMode.Normal));
}
}
}
unorderedList.InnerHtml = htmlSummary.ToString();
return messageSpan + unorderedList.ToString(TagRenderMode.Normal);
}
This is using the existing extension method from within the framework and changing the tag that is inserted into the list. This is a quick sample and there are some things to consider before using this:
This does not encode the error message as I have used the existing html.ValidationMessage. If you need to encode the message then you would need to include so code to provide default messages and any localization requirements.
Due to the use of the existing ValidationMessage method there is a span tag within the anchor. If you want to tidy your HTML then this should be replaced.
This is the most complicated of the usual overloads. If you want to use some of the simpler ones such as html.ValidationSummary() then you would need to create the relevant signatures and call to the method provided.

How to display textarea's data in a table

I am using ASP.MVC 3. I have a view that has a textarea on it. I captures data in it, and when I want a new paragraph I wil press enter twice. After all my data is entered I save the text to the database.
In my details view I would display the data in a like:
<tr>
<td valign="top"><label>Body:</label></td>
<td>#Model.Body</td>
</tr>
Now the text displays as 1 paragraph even though in my textarea (when I captured the data) it seemed liked paragraphs.
How would I get the data to display as paragraphs in my table like what I captured it in my textarea. I'm assuming that I have to search for carriage returns and replace them with break tags?
I'm assuming that I have to search for carriage returns and replace them with break tags?
Yes, your assumption is correct. You could use a custom HTML helper:
public static IHtmlString FormatBody(this HtmlHelper htmlHelper, string value)
{
if (string.IsNullOrEmpty(value))
{
return MvcHtmlString.Empty;
}
var lines = value.Split('\n'); // Might need to adapt
return htmlHelper.Raw(
string.Join("<br/>", lines.Select(line => htmlHelper.Encode(line)))
);
}
and then:
#Html.FormatBody(Model.Body)
UPDATE:
And here's an example of how this method could be unit tested:
[TestMethod]
public void FormatBody_should_split_lines_with_br_and_html_encode_them()
{
// arrange
var viewContext = new ViewContext();
var helper = new HtmlHelper(viewContext, MockRepository.GenerateStub<IViewDataContainer>());
var body = "line1\nline2\nline3<>\nline4";
// act
var actual = helper.FormatBody(body);
// assert
var expected = "line1<br/>line2<br/>line3<><br/>line4";
Assert.AreEqual(expected, actual.ToHtmlString());
}

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