How do I use razor to make something a link conditionally? - asp.net-mvc

I have a razor view where I'm currently using code that looks something like this:
#if(Model.IsLink)
{
<a href="...">
}
Some text that needs to appear
#if(Model.IsLink)
{
</a>
}
This works but the code doesn't feel clean. Is there a better/more accepted way of accomplishing this pattern?

Here is a simple method I think is a little cleaner.
Set the text to a variable at the top of the view:
#{
var someText = "Some text that must appear";
}
Then output conditionally:
#if (Model.IsLink)
{
<a href='#'>#someText </a>
}
else
{
#someText
}
The multi-line if statement above avoids doing string construction with HTML, but if you want to condense the syntax down to one line you can do this.
#Html.Raw(Model.IsLink?String.Format("<a href='#'>{0}</a>",someText):someText)

You could create a custom HtmlHelper method. See the reference on how to do that here: http://www.asp.net/mvc/tutorials/older-versions/views/creating-custom-html-helpers-cs
Your method could take the "IsLink" boolean as a parameter, and use it to either output an anchor or plain text. Here's a sample of what that might look like:
namespace MvcApplication1.Helpers
{
public static class LabelExtensions
{
public static string LinkableText(this HtmlHelper helper, bool isLink, string text, string url = null)
{
return isLink ? string.Format("<a href='{0}'>{1}</a>", url, text) : text;
}
}
}

In HTML 5 it is valid to have markup like this if you do not have a link
<a>some text</a>
and markup like this if you do
some text
And as of razor v2 your null conditional attributes take care of the href, so your code can be like this.
some text
If #link is null the href attribute will be omitted.

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.

In MVC, How can I develop something like the old school User Control?

I have a TreeView, wish is used to display some hierarchy. Also, I have some behaviors implemented, such as if the collapsed nodes will be opened when the parent is checked, or if it's lazy loading or will load everything automatically. But I don't want to let these behaviors hard coded. I want to create a customizable control. Something like Telerik Kendo UI does. They have a control developed, that can be used like this:
#(Html.Kendo().MaskedTextBox()
.Name("phone_number")
.Mask("(999) 000-0000")
.Value("555 123 4567")
)
Notice that you can pass some options to it.
Also, I want to be able to pass the name of the action that will populate my TreeView using async. So, if I use this component at, for example, mysite/myController/indexAction, I want to pass a name of the action who will populate my component asynchronous. Let's exemplify. I want something like this:
#(Html.Foo().HierarchyView()
.Name("myTree")
.AllowCheckBoxes(true)
.PopulateChildrenOnCheck(true)
.Async("GetChildrenAsync")
)
So, I can implement at myController an action
string GetChildrenAsync(int idKey) {
var nodes = new List<HierarchViewNodes>();
(...) //implementation to populate the children nodes
return JsonConvert.SerializeObject(nodes);
}
So, it would be an start for my customizable control. Of course I can extend it a lot.
I've searched and learned about RenderPartial and RenderAction, but I can't figure out yet how I can fully use it to make really reusable controls like the one I explained.
You might want to take a look at making some custom HTML helpers. For example, yours might look something like this:
public static MvcHtmlString MyTreeView(this HtmlHelper html, params...){
var myHtmlContent = ....;
// .. implement your functionality and generate the html to put in your view
return MvcHtmlString.Create(myHtmlContent);
}
and you could use it in your Razor view like this:
<div>#Html.MyTreeView(params...)</div>
That is a very simple example, but hopefully it puts you on the right track.
I'd suggest some HTML extensions, as rwisch45 said, but protected (and organized) within a separate "sub-helper", like Kendo and DevExpress:
public static class HtmlExtensions
{
public static static MyCustomHelper CustomHelper(this HtmlHelper htmlHelper)
{
return new MyCustomHelper(htmlHelper);
}
}
public class MyCustomHelper
{
HtmlHelper _HtmlHelper;
public MyCustomHelper(HtmlHelper htmlHelper) { _HtmlHelper = htmlHelper; }
public MvcHtmlString WriteSomethingInteresting(string value)
{ return new MvcHtmlString(value); }
public MyCustomGrid CreateMyGrid(object gridOptions)
{
// I won't show now how to transform dynamic into type.
// You can find that on SO quite easy.
var typedOptions = TransformDynamicIntoClass<GridOptions>(gridOptions);
return new MyCustomGrid(typedOptions);
}
}
public class MyCustomGrid : IHtmlString
{
public string ToHtmlString()
{
// Return your grid as an HTML object!
}
}
This way you'll have:
#Html.CustomHelper().MyCustomGrid(new { Option1 = "", Option2 = "", ...... });
You may, however, play a little with IDisposable, to have something like:
#using(Html.CustomHelper().MyCustomGrid(....))
{
// Dunno what could come here for a grid (poor sample) but maybe for a pane helper?
}

MVC Razor: Helper method to render alternate content when empty

I've got some data, and sometimes that data is blank. Instead of making a bunch of crazy logic on my view I'd rather use a helper method that will render the data if it exists, and render some HTML that just says "N/A" when the string is empty/null.
Ideal syntax: #Helpers.RenderThisOrThat(Model.CustomerPhone)
If the Model.CustomerPhone (a string) is empty it will render this alternate HTML instead: <span class='muted'>N/A</span>
Here's what we have so far:
#helper RenderThisOrThat(string stringToRender, string methodToGetAlternateText = null)
{
#RenderThisOrThat(MvcHtmlString.Create(stringToRender), methodToGetAlternateText)
}
#helper RenderThisOrThat(MvcHtmlString stringToRender, string methodToGetAlternateText = null)
{
if (string.IsNullOrWhiteSpace(stringToRender.ToHtmlString()))
{
if (!string.IsNullOrWhiteSpace(methodToGetAlternateText)) {
#methodToGetAlternateText
}
<span class='muted'>N/A</span>
}
#stringToRender
}
This works just fine until we want to pass something other than a string into either parameter. For example when we have an email address we want it to be a link to that email and not just the string of the email.
#Helpers.RenderThisOrThat(##Html.DisplayFor(m => m.CustomerEmail))
It gives us the error: "Cannot convert lambda expression to type 'string' because it is not a delegate type"
We are at a loss for how to make this work... any help here?
You're looking for a helpers that will take a string and:
If the string is not empty, render that string.
If the string is not empty, render a given template.
If the string is empty, render "N/A" html.
If the string is empty, render a given template.
When passing a razor block to a function as a parameter, razor packages the block as Func. Change the parameters in the helper functions to take that type of delegate and don't forget to call those delegates (I chose to pass null).
These helpers should handle those scenarios.
Solution
#helper RenderThisOrThat(string stringToRender, Func<object, IHtmlString> leftTemplate = null, Func<object, IHtmlString> rightTemplate = null)
{
var shouldRenderLeft = !string.IsNullOrWhiteSpace(stringToRender);
leftTemplate = leftTemplate ?? (o => MvcHtmlString.Create(stringToRender));
#RenderThisOrThat(shouldRenderLeft, leftTemplate, rightTemplate)
}
#helper RenderThisOrThat(bool shouldRenderLeft, Func<object, IHtmlString> leftTemplate, Func<object, IHtmlString> rightTemplate = null)
{
var shouldRenderRight = !shouldRenderLeft;
if (shouldRenderRight)
{
if (rightTemplate != null)
{
#rightTemplate(null)
}
else
{
<span class='muted'>N/A</span>
}
}
else
{
#leftTemplate(null)
}
}
Examples
1. #Helpers.RenderThisOrThat(Model.StringWithBob)
2. #Helpers.RenderThisOrThat(Model.StringWithNull)
3. #Helpers.RenderThisOrThat(Model.StringWithBob, #<span>I'm #Model.StringWithBob</span>)
4. #Helpers.RenderThisOrThat(Model.StringWithNull, #<span>I'm #Model.StringWithBob</span>)
5. #Helpers.RenderThisOrThat(Model.StringWithBob, #<span>I'm #Model.StringWithBob</span>, #<span>What about Bob?</span>)
6. #Helpers.RenderThisOrThat(Model.StringWithNull, #<span>I'm #Model.StringWithBob</span>, #<span>What about Bob?</span>)
Will output:
Bob
<span class='muted'>N/A</span>
<span>I'm Bob</span>
<span class='muted'>N/A</span>
<span>I'm Bob</span>
<span>What about Bob?</span>
This is an awfully complex solution to a simple problem. You don't need to create complex views, in fact, you should be using an Editor/DisplayTemplate, then you put your logic in the template and it's done once, without all the need for extra inclusion of helper functions, or anything else.
You can also go a step further here, because in this case you're rendering an email address. You apply a DataType attribute to your model and then specify an Phone Number rendering type.
public class MyModel {
[DataType(DataType.PhoneNumber)]
public string PhoneNumber {get;set;}
}
Then you create a folder in ~/Views/Shared called DisplayTemplates and in that folder create a file called PhoneNumber.cshtml, in it you do this:
#model string
#if (string.IsEmptyOrWhiteSpace(Model)) {
#:<span class='muted'>N/A</span>
} else {
#: <span>#Model</span>
}
Then, in your view:
#Html.DisplayFor(x => x.PhoneNumber)
That's it, no complex logic in your view, no convluted helper functions everywhere. This is simple, easy, and maintainable. You can do the same for Email address as there is an EmailAddress datatype as well.
MVC has a lot of very good functionality built-in that most people simply do not use, because they haven't spent any real time learning it.

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;
}

How to use ASP.NET MVC Html Helpers from a custom helper?

I have several pages listing search results, for each result I would like to display I want to create a custom View Helper in order to avoid duplicating the display code.
How do I access the convenient existing view helpers from my custom view helper? I.e. in my custom view helper I would like to use Url.Action(), Html.ActionLink, etc. How do I access them from my custom view helper?
using System;
namespace MvcApp.Helpers
{
public class SearchResultHelper
{
public static string Show(Result result)
{
string str = "";
// producing HTML for search result here
// instead of writing
str += String.Format("{1}", result.id, result.title);
// I would like to use Url.Action, Html.ActionLink, etc. How?
return str;
}
}
}
using System.Web.Mvc gives access to HtmlHelpers, but non of the convenient methods like ActionLink seem to be present.
This example should help you. This helper renders different link text depending on whether the user is logged in or not. It demonstrates the use of ActionLink inside my custom helper:
public static string FooterEditLink(this HtmlHelper helper,
System.Security.Principal.IIdentity user, string loginText, string logoutText)
{
if (user.IsAuthenticated)
return System.Web.Mvc.Html.LinkExtensions.ActionLink(helper, logoutText, "Logout", "Account",
new { returnurl = helper.ViewContext.HttpContext.Request.Url.AbsolutePath }, null);
else
return System.Web.Mvc.Html.LinkExtensions.ActionLink(helper, loginText, "Login", "Account",
new { returnurl = helper.ViewContext.HttpContext.Request.Url.AbsolutePath }, null);
}
EDIT:
All you would need to do to to access the Url.Action() method would be to replace the this HtmlHelper helper param with something like this UrlHelper urlHelp and then just call urlHelp.Action(...
Hope this helps.
A simple gravatar html helpler, your class needs to be static also.
public static string GetGravatarURL(this HtmlHelper helper, string email, string size, string defaultImagePath)
{
return GetGravatarURL(email, size) + string.Format("&default={0}", defaultImagePath);
}
you can extend the default HtmlHelper and UrlHelper just with an extension method (so you have the xxxHelper as first param in your method).
Or you can just create your base view with the method you want and use the Html or URL variable of the view.
In my opinion, you shouldn't be trying to use ActionLink within code. The whole concept of MVC is to separate logic from display, so you should try to stick with that.
I would suggest you pass the result object through to the view (maybe through ViewData) and then parse the result inline within the view. e.g.
<%= Html.ActionLink(result.title,"/showresult/" + result.id, "myController") %>

Resources