ASP.Net MVC3 - Pass razor markup as a parameter - c#

I have a helper called EditableArea which provides a user with a runtime-editable div (via JS). EditableArea helper checks if an editable area (not related to MVC's Area) with the specified ID exists in the DB, if so then it renders the area's HTML, otherwise it displays the default markup specified as a parameter of the helper:
#Html.EditableArea(someId, "<p>Click to edit contents</p>")
It all works ok, but I'd like to change it so that the default markup is specified not as a string but in razor syntax, something like:
#using (Html.EditableArea(someId))
{
<p>Click to edit contents</p>
}
Or something similar, like the way #sections work in MVC3.
How can I achieve that?
I can make an IDisposable which in its Dispose closes the TagBuilder, etc., but using this approach the markup will still be rendered (I can clear the rendered contents in the Dispose() but the code block would still run unnecessarily, which I'd like to avoid).
Is there some other way to pass a razor block to the helper, which may or may not be actually rendered?

Here's an example I use to render jQuery Template markup by passing in a template Id and razor-style syntax for the template itself:
public static MvcHtmlString jQueryTmpl(this HtmlHelper htmlHelper,
string templateId, Func<object, HelperResult> template)
{
return MvcHtmlString.Create("<script id=\"" + templateId +
"\" type=\"x-jquery-tmpl\">" + template.Invoke(null) + "</script>");
}
and this would be called with
#Html.jQueryTmpl("templateId", #<text>any type of valid razor syntax here</text>)
Basically just use Func<object, HelperResult> as your parameter and template.Invoke(null) (with arguments if necessary) to render it. Obviously you can skip the call to .Invoke() to avoid rendering the "default" markup.

Just to expand on the accepted answer, as it took me quite a while to resolve a similar problem and this is the question which popped up. What I really need was a #helper, which would accept razor text, as the template should contain quite some code. I played around for a long while trying to use several versions of type #helper item(Func<object, HelperResult> input), which I found on the web, with no success. Therefore I went for an approach like:
namespace project.MvcHtmlHelpers
{
public static class HelperExtensions
{
public static MvcHtmlString RazorToMvcString(this HtmlHelper htmlHelper, Func<object, HelperResult> template)
{
return MvcHtmlString.Create(template.Invoke(null).ToString());
}
}
}
and
#project.MvcHtmlHelpers
#helper item(other input, MvcHtmlString content)
{
<div class="item">
...other stuff...
<div class="content">
#content
</div>
</div>
}
and use this via
#item(other input, #Html.RazorToMvcString(#<text>this is a test</text>))
Now I can use the helper template for both Razor input, but I can also drop in partial views, which is handy at some points. As I am no expert there might be better options, but it seems like a flexible approach to me.

In case you're wondering this is how to do it in asp.net core 3.1
#{
void TemplateFunc(Func<object, IHtmlContent> template)
{
<div>#template(null)</div>
}
}
Then in markup you can use it as
<div>
#{TemplateFunc(#<div>123</div>);}
</div>

Taking this further, it is possible to pass the markup directly to a helper, without an extension method.
#helper HelperWithChild(Func<object, HelperResult> renderChild)
{
<div class="wrapper">
#renderChild(this)
</div>
}
#HelperWithChild(#<h1>Hello</h1>)
For multi-line markup <text> is required as well:
#HelperWithChild(#<text>
#AnotherHelper()
<h1>
With more markup
</h1>
</text>)
#helper AnotherHelper()
{
<p>
Another helper
</p>
}
Though I'm not sure how this will play out with Model - my helpers only use their parameters.

Related

Razor engine display angle brackets in markdown [duplicate]

I am trying to generate emails with HTML content. this content has already gone through sanitation so I am not worried in that regard, however when I call:
Razor.Parse(template, model);
on the following Razor template:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<body>
#(new System.Web.HtmlString(Model.EmailContent))
</body>
</html>
the email that is outputted is HTMl encoded, but I need it decoded. How can I accomplish this?
RazorEngine, like MVC's Razor View Engine, will automatically encode values written to the template. To get around this, we've introduce an interface called IEncodedString, with the default implementations being HtmlEncodedString and RawString.
To use the latter, simply make a call to the inbuilt Raw method of TemplateBase:
#Raw(Model.EmailContent)
FYI I have a fork that includes the #Html.Raw(...) syntax here:
https://github.com/Antaris/RazorEngine/pull/105
I am using RazorEngine 3.8.2 and #Raw(Model.Content) is working perfectly fine for me.
If you have a custom base class for your templates, you can code Write method to behave similar to normal MVC template: if the output value is IHtmlString it should not encode it.
Here's the code I'm using in my TemplateBase class:
// Writes the results of expressions like: "#foo.Bar"
public virtual void Write(object value)
{
if (value is IHtmlString)
WriteLiteral(value);
else
WriteLiteral(AntiXssEncoder.HtmlEncode(value.ToString(), false));
}
// Writes literals like markup: "<p>Foo</p>"
public virtual void WriteLiteral(object value)
{
Buffer.Append(value);
}
Built a wrapper for RazorEngine that adds in support for #Html.Raw() and #Html.Partial()
https://github.com/b9chris/RazorEngineComplete

How can I insert HTML tags in C# string property?

Not sure how if it is possible, but I have this in a class:
public string TextNotIncluded
{
get
{
return ("which is <u>not</u> included in the Quote");
}
}
The <u> and </u> are being displayed in my view, rather than the word not being underlined. I am not familiar with C#.
Can anyone provide a quick answer?
Thanks.
Edit:
I am just calling this in my view thusly: #MyClass.TextNotIncluded. Wrapping it with #Html.Raw is not efficient in my case because I have this sprinkled throughout dozens of views.
There's nothing fundamentally wrong with doing that but it probably won't render the way you're expecting.
You can use #Html.Raw as others have suggested, but I think it's better to explicitly declare your model in such a way as to indicate that it may contain html. You probably want to use the MvcHtmlString class for this instead:
public MvcHtmlString TextNotIncluded
{
get { return MvcHtmlString.Create("which is <u>not</u> included in the Quote"); }
}
Then in your view you can just use:
#Model.TextNotIncluded
If you're using Razor, strings are HTML-encoded by default - you'll need to use Html.Raw to turn off the encoding:
#Html.Raw(x.TextNotIncluded)
In the ASPX engine, you would use <%= %>
<%= x.TextNotIncluded %> - this gives you the raw text
<%: x.TextNotIncluded %> - this HTML-encodes your text - you don't want this.
To output raw HTML, use the Raw HTML helper:
#Html.Raw(TextNotIncluded)
This helper doesn't HTML encode the input, so be careful when using it.
You need to HTML encode the string. Most are recommending the MVC approach, but I would make it more independent of the presentation layer.
public string TextNotIncluded {
get {
return System.Web.HttpUtility.HtmlEncode("which is <u>not</u> included in the Quote");
}
}
You can use either
#Html.Raw(Model.TextNotIncluded)
or
#MvcHtmlString.Create(Model.TextNotIncluded)
in your view.
But it would be better to alter the return type of the property:
public MvcHtmlString TextNotIncluded
{
get
{
return MvcHtmlString.Create("which is <u>not</u> included in the Quote");
}
}

Html Extension methods are not accessible in MVC Partial Pages

Hi I'm trying to create a declarative Html helper method inside MVC partial page, everything works fine. But when I want to use built-in Html extension methods, I see there are no extension methods. Also I checked my view's webconfig file to add System.Web.Mvc.Html namespace. everything is OK, but I don't know why it's not working.
Any advice will be helpful.
Edit : Here is my code :
#using WebVoter.ViewModel
#using System.Web.Mvc.Html
#helper GetVoteList(IList<VoteQuestionViewModel> voteQuestionList)
{
<div class="head-panel">
#*For example ActionLink is not accessible here*#
#Html.ActionLink(....);
</div>
}
Inside Razor helpers you do not have reference to HtmlHelper. You need to pass it as parameter from the calling code:
#helper GetVoteList(HtmlHelper html, IList<VoteQuestionViewModel> voteQuestionList)
{
<div class="head-panel">
#html.ActionLink(....)
</div>
}
and when you want to call this helper from some view you need to pass the reference:
#GetVoteList(Html, ...)
Personally I've always preferred writing extension methods to the HtmlHelper class instead of using those inline Razor helpers. They would then be used as standard helpers:
#Html.GetVoteList(...)
The advantage is that you are no longer tied to Razor. Extension methods are view engine agnostic and make transition to other view engines less painful because they are C# code. Another benefit is that they can be unit tested.
There is a web.config under Views folder. Can you modify this config too?

When is it appropriate to create HTML code in the controller (or back end)

I am doing some complex logic involving loads of recursion to create a (complex) piece of HTML.
I started off doing this in the View using functions in Razor because I felt as HTML it belonged there.
But as it started getting more complex I thought I would rather do it in back-end code. Which it currently is.
It still feels a bit smellish though, and I am wondering if I should move it to the View again (which obviously clutters the view)
Which technically is more correct? When is it appropriate to use back-end code to generate HTML?
Thanks for your input.
create more granular partial views and partial models to maintain MVC pattern.
when your correctly select a model for your view, even partial, generating HTML in view is not a problem. you may end up with 20 views and 5 more models, but controller will be just selecting views and populating models which is good.
Don't do it in controller. You can extend the HtmlHelper class and do the stuff there. For example if you are using a paging helper.
Create a static class HtmlHelpers
namespace YourMvcApplication.WebUI.HtmlHelpers
{
public static class PagingHelpers
{
public static MvcHtmlString PageLinks(this HtmlHelper html,int totalPages)
{
StringBuilder result = new StringBuilder();
// do the complex logic to create dynamic html and append to
// String Builder
return MvcHtmlString.Create(result.ToString());
}
}
}
Add reference to this class in all views in web.config.
<namespaces>
<add namespace="YourMvcApplication.WebUI.HtmlHelpers"/>
</namespaces>
Use and resuse this Html Helper methods wherever required.
<div>
#Html.PageLinks(Model.TotalPages)
</div>

Add css class to Html.EditorFor in MVC 2

I'm trying to add a css class to a textbox. This is what I have in my view:
<%: Html.EditorFor(m => m.StartDate) %>
I tried following the instructions at this link by making my code:
<%: Html.EditorFor(m => m.StartDate, new { #class: "datepicker" }) %>
But I get a compiler error saying:
Syntax error, ',' expected
What am I doing wrong here?
With MVC3, I kept banging my head because I couldn't get this to work. I didn't want to create a whole EditorTemplate for just adding one class.
Well, instead of using EditorFor, use TextBoxFor, with of course the equals sign like so:
#Html.TextBoxFor(m=> m.ZipCode, new { #class = "zip" })
I would HIGHLY suggest using Editor Templates. It's definitely the "right" way to style your EditorFor.
You can tell a model property to use an Editor Template in two different ways.
The first (the simplest) is to create an editor template for a certain data type - DateTime for example.
The second way to do it is to set it declaratively in your DataAnnotations by using a UIHint.
Edit
I'd also like to add that you should use the "date" type in your input field so that even when JavaScript is disabled, your user can stills see a native datepicker (only valid on modern HTML5 browsers)
<input id="meeting" type="date" value="2011-01-13"/>
I guess a quick and dirty way to do this would be in jQuery, yes?
$(document).ready(function () {
$('#StartDate').addClass('datepicker');
});
Ideally, you should use the Editor Templates. I got around this issue by using the Editor Template inside the MvcHtmlString.Create() which will let you rebuild the actual HTML code. Of course, you'll want to copy everything in the "class" section to keep the Editor Template as useful as possible.
I tried many of the suggestions above, but eventually, I settled on this, because I think it's less complicated and it lets me continue using Editor Templates:
#MvcHtmlString.Create(Html.EditorFor(m => m.StartDate).ToString().Replace("class=\"text-box single-line\"", "class=\"text-box single-line datepicker\""))
I know this is an old question but thought I could contribute so here goes. I had the same problem and wanted to avoid making Editor Templates. I just wanted a generic handle everything solution that would allow me to specify html attributes when using Html.EditorFor in a view.
I really liked CIAs answer, but I expanded on it a bit so that you can pass in any attributes you need. I created an extra Html.EditorFor method that accepts html attributes:-
public static class EditorForExtentions
{
public static MvcHtmlString EditorFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, Object htmlAttributes, bool extendAttributes)
{
string value = html.EditorFor(expression).ToString();
PropertyInfo[] properties = htmlAttributes.GetType().GetProperties();
foreach (PropertyInfo info in properties)
{
int index = value.ToLower().IndexOf(info.Name.ToLower() + "=");
if (index < 0)
value = value.Insert(value.Length - (value.EndsWith("/>") ? 2 : 1), info.Name.ToLower() + "=\"" + info.GetValue(htmlAttributes, null) + "\"");
else if (extendAttributes)
value = value.Insert(index + info.Name.Length + 2, info.GetValue(htmlAttributes, null) + " ");
}
return MvcHtmlString.Create(value);
}
}
You can call it in a view like this
<%=Html.EditorFor(m => m.StartDate, new { #class = "datepicker" }, true)%>
It uses the normal Html.EditorFor method to get the html string, then injects the html attributes needed.
There is no overload for EditorFor that allows you to set HtmlProperties.
(IDictionary htmlAttributes)
This link explains how to do it:
http://aspadvice.com/blogs/kiran/archive/2009/11/29/Adding-html-attributes-support-for-Templates-2D00-ASP.Net-MVC-2.0-Beta_2D00_1.aspx
I was looking for a solution to apply a style to a specific box generated by the #HTML.EditorFor helper method.
The question was regarding setting a CSS class for #HTML.EditorFor but for anyone who wants to edit the style for a single element.. you can, for example, try this:
In my block, I added a style based on the ID generated by the helper:
..
<style>
#EnrollmentInfo_Format
{
width:50px;
font: normal 100% 'Lucida Grande',Tahoma,sans-serif;
font-size: 11px;
color: #2e6e9e;
}
</style>
and then in my page (i'm doing this in a partial view):
#Html.EditorFor(e => e.EnrollmentInfo.Format)
Here's a very simple solution: Remove the double quotes from "datepicker" and retype them back into Visual Studio and it should work.
I had the same problem. I copied/pasted sample code from the web and the code had a special type of quote which caused the "," syntax problem. I know it's really not obvious.

Categories