I am experimenting with custom HtmlHelpers, but can't get even a basic one to work correctly. My code (just for testing sake) looks like this -
My class -
namespace HtmlHelpers.Extensions
{
public static class Helpers
{
public static string MySubmitButton(this HtmlHelper helper)
{
return String.Format("<input type=\"submit\" value=\"Submit This\">");
}
}
}
In my view -
#using HtmlHelpers.Extensions;
#Html.MySubmitButton()
This I believe should generate a simple submit button, but instead, it justs writes the following text to the screen -
<input type="submit" value="Submit">
I inspected the element, and for some reason the entire input element is being surrounded with double quotes.
Anyone know why? Thanks!
I believe you should be returning a MvcHtmlString class. Try
public static MvcHtmlString MySubmitButton(this HtmlHelper helper)
{
return MvcHtmlString.Create("<input type=\"submit\" value=\"Submit This\">");
}
although there's probably better ways to do this using a TagBuilder if you look at examples online, or the MVC source code since it's open source, you can look at their html helpers(although they are pretty complicated due to the way they layer them).
To directly answer your question of "why" it was displaying that as if it were a string, is Razor tries to be safe and convert anything you display as text instead of HTML/script. For example, #Model.PeronName will escape any characters in the peron's name with HTML character codes. Consider if there was no protection like this, and one of your users changed their name to be <script>someDangerousJavascriptThatWouldChangeCurrentUsersPassword()</script>, then posted on your forum or anywhere their name would appear, then other users visit the page, and that javascript runs and POSTS a change password form for that current user's password to some password that the hacker chose in the script. There are a wide variety of complicated attacks like this, or users might accidentally enter angle brackets(and while fairly harmless if treated as HTML they will mess up your page display).
For that reason MVC will assume just about any string is not HTML and thus replace things like <script> with <script> which basically is a way of saying "this is not html/script, I want you to display the less-than symbol and greater than symbol". If you wanted to display something as HTML, there is a #Html.Raw() helper for that, and it won't clean output, and thus should never be used with a string that you concatenated together from any data that a user my supply.
Related
I am looking for best practices for design razor view with MVC.
which would be better option:
HtmlHelper extension methods
#Html.TextBox("txtName")
or
write the html directly
<input type"text" id="txtName" name="txtName" />
I found 2 diferent links.
The first one http://blogs.msdn.com/b/aspnetue/archive/2010/09/17/second_2d00_post.aspx says DO use HTMLHelper extension methods.
and the second one http://codeclimber.net.nz/archive/2009/10/27/12-asp.net-mvc-best-practices.aspx says 10 – Write HTML each time you can
so i am a little cofused
Even the name HtmlHelper should already give you a hint whether you should use it or not. Do you want help? If not, just write html from the scratch. It does not really matter how the html was generated: from the scratch or using html helper. What matter is that it was generated with correct names of the inputs so that model binder can bind these inputs to the model.
For example, suppose you have the following Model that will be passed to the view and that will be received on the POST:
public class SomeModel
{
public Customer Customer { get; set; }
}
public class Customer
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
In order to make sure that your inputs will be binded to the model you need three inputs on your page:
<input type="hidden" id="whatever" name="Customer.Id" />
<input type="text" id="whatever" name="Customer.FirstName" />
<input type="text" id="whatever" name="Customer.LastName" />
Having this html markup will assure proper model minding. However, you can achieve this markup by using HtmlHelpers, which is a lot easier:
#Html.HiddenFor(m => m.Customer.Id)
#Html.TextBoxFor(m => m.Customer.FirstName)
#Html.TextBoxFor(m => m.Customer.LastName)
This will not only give you proper name attributes on every input, but also assign id attributes accordingly so you don't have to do that all by your self.
It appears that the author from the second article suggests to never use HtmlHelpers for two reasons:
the learning purposes: I assume by saying "web developers have to be
comfortable writing HTML" he means that developer should know
exactly what html markup is required for proper model binding.
the fear of black box: It seem that author is afraid that improper html
markup will be generated by using HtmlHelpers or he just does not
know what html will be generated.
I disagree with his phrase: "HtmlHelpers whose only reason of living is hiding the HTML away". I'd rather say "HtmlHelpers whose only reason of living is helping writing Html markup"
Summary:
HtmlHelpers help you write proper html markup, which is why I suggest you using it.
Since you're using Razor, I would make the most of what it has to offer, and the HtmlHelper extensions allow you to write html quicker and easier in a lot of places.
There may be times when you have to use Html instead, where you might want to include tags in an anchor and cannot use #Html.ActionLink, for example.
But where you can achieve the same result with either approach, I'd recommend you go with Razor.
Html helpers are not cosmetic code snippets that just save time. Consider choosing the appropriate editor based on model property types, and - what is also very important - they help in client validation, provided you include jquery.unobtrusive-ajax.js and jquery.validate.js.
The last cannot be achieved by simply writing HTML.
From what I said it can be easily derived that Html helpers "know" about the model, while plain markup does not.
At the end it is up to you to decide what to use, but knowing more about Html helpers is better when making a decision.
Consider the following string if I need to localize it:
You need to write <b>#ViewBag.CharacterAmount</b> characters to be able to
hand-in this homework. You're still missing <b id="charactersRemaining">
#ViewBag.CharacterAmount</b> characters to reach this limit.
What would be the best approach? Using string.Format is a bit complex, since ASP .NET MVC escapes HTML code, and besides, I'd rather be free of HTML code in my resource files. However, I still need to be able to refer to those values inside the b tags from JavaScript.
Any ideas? What is your approach to this when you do localization?
You could write a custom helper:
public static class ResourcesExtensions
{
public static IHtmlString Resource(this HtmlHelper htmlHelper, string message, params object[] args)
{
var parameters = args.Select(x => htmlHelper.Encode(x)).ToArray();
return new HtmlString(string.Format(message, parameters));
}
}
As you can see the HTML helper encodes only the values. We have full control over the rest of the message because it is in the resources file and we suppose that it is valid HTML, so no problem with XSS.
and then have a resources file to your project which will contain for example the following key:
MyMessage = You need to write <b>{0}</b> characters to be able to hand-in this homework. You're still missing <b id="charactersRemaining">{1}</b> characters to reach this limit.
and then don't forget to mark this resources file with the PublicResXFileCodeGenerator custom tool so that Visual Studio generates a public class that will allow you to access the properties in the view.
and finally in the view:
#Html.Resource(Resources.MyMessage, (int)ViewBag.CharacterAmount, (int)ViewBag.CharacterAmount)
The reason you need to cast is because extension method cannot dispatch dynamic arguments. But obviously that's not a problem at all because you shouldn't be using ViewBag/ViewData but you should be using view models and strongly typed view models so in your real code you will have:
#Html.Resource(Resources.MyMessage, Model.CharacterAmount, Model.CharacterAmount)
One downside with this approach is that we have moved some markup in the resources file which unfortunately might render the views a little less understandable and when we need to modify it, we should do this in all localized versions.
Another approach of course consists into putting in your resources file every distinct part of this markup and then:
#Resources.YouNeedToWrite <b>ViewBag.CharacterAmount</b> #Resources.StillMissing
<b id="charactersRemaining">ViewBag.CharacterAmount</b> #(Resources.ToReachLimit).
i have searched the web relentlessly for this and have not found anything - which is surprising because i would think it is such a common scenario!
Basically, on my model i have a DateTime field which i wish the user to populate through a form. I am using the Html helper to render all other parts of the form (along with validation)
So this question is in two parts...
Html Helper
Firstly, is there any way to use the Html helper to split the DateTime field to be rendered as the three constituent parts of a date: day, month, year (since i do not care about the time part). This could be rendered as text boxes, drop down lists or a combination of both.
Model Binding
And then when the form is posted, what is the best approach for binding back up to the model? I have seen Scott Hanselmann's solution to this, but it seems a little bloated for what i need - i was hoping for a slightly more elegant solution. Is it recommended to extend DefaultModelBinder and set that as default binder (since all dates would be handled in this way) or write a class that implements IModelBionder and set it as the default binder for the DateTime type?
Thanks for all the help in advance :-) i'm loving MVC but it's infuriating me that something so trivial is causing so much headaches!
think i've worked out a decent solution to this, so i will provide an answer for any who stumble across this in future!
With regards to the Html helper, i somehow completely overlooked creating an extension method! So eventually when it occurred to me, i wrote an extension method which basically makes calls to other methods of the Html helper to provide three fields (whether you use drop down of text inputs is your choice)
public static string DateTime(this HtmlHelper helper, string name)
{
StringBuilder builder = new StringBuilder();
string dayName = name + ".Day";
string monthName = name + ".Month";
string yearName = name + ".Year";
builder.Append(helper.TextBox(dayName));
builder.Append(helper.DropDownList(monthName, typeof (Months)));
builder.Append(helper.TextBox(yearName));
return builder.ToString();
}
As for the binding after a form post, i simply created a class which implemented IModelBinder and set it as default for DateTime type on application start. This can then be overridden at a controller action level should a different method of binding be required. This was essentially a simpler version of Scott's binder which i linked to in my question.
Works like a charm at the moment (if a little simplistic), but i'm sure it'll be enough of a foundation for anyone else who is mystified by this problem!
Our designers have come up with button styles for an application which require the addition of <span> tags inside the <a> tags of our links.
In ASP.NET we implemented this by adding an App_Browsers entry for Link Buttons.
How would I go about doing this in ASP.NET MVC?
I've contemplated creating my own versions of all of the various HTML helper functions for creating ActionLinks and RouteLinks but this seems to be quite a 'brute force' way of doing things.
Is there a nice elegant way of doing it?
I know we could write some simple jQuery to do it, but we'd rather have the markup coming out of the server correctly in the first place.
Actually I think writing a new helper is exactly the way I would go. Seems to me that that's exactly what they are there for and it makes them very re-usable too.
You could always write one extension method, that takes another one (one of the built-in ones) as an argument, and wrappes the <span> around your link text before calling it. It should be quite easy to do with lambdas...
public static string SpanLink(this HtmlHelper helper,
string linkText, object args, Action<string> action)
where TController : IController
{
action("<span>" + linkText + "</span>", args);
}
And to call it:
<%= Html.SpanLink<HomeController>("link text", (s) => Html.ActionLink<HomeController>(c => c.Index(s));
(This code is typed directly into the answer field of SO - I haven't even checked it to make sure it compiles. So bear with me if it doesn't work on the first try...)
In an ASP.NET MVC view I'd like to include a link of the form:
Link text <span>with further descriptive text</span>
Trying to include the <span> element in the linkText field of a call to Html.ActionLink() ends up with it being encoded (as would be expected).
Are there any recommended ways of achieving this?
You could use Url.Action to build the link for you:
link text <span>with further blablah</span>
or use Html.BuildUrlFromExpression:
text <span>text</span>
if you like using Razor, this should work:
link text <span>with further blablah</span>
Another option is to render your action link to an MvcHtmlString as per normal, using either HTML.ActionLink, or Ajax.ActionLink (depending on your context), then write a class that takes the rendered MvcHtmlString and hacks your html link text directly into the already rendered MvcHtmlString, and returns another MvcHtmlString.
So this is the class that does that: [please note that the insertion/substitution code is VERY simple, and you may need to beef it up to handle more nested html]
namespace Bonk.Framework
{
public class CustomHTML
{
static public MvcHtmlString AddLinkText(MvcHtmlString htmlString, string linkText)
{
string raw = htmlString.ToString();
string left = raw.Substring(0, raw.IndexOf(">") + 1);
string right = raw.Substring(raw.LastIndexOf("<"));
string composed = left + linkText + right;
return new MvcHtmlString(composed);
}
}
}
And then you would use it in the view like this:
#Bonk.Framework.CustomHTML.AddLinkText(Ajax.ActionLink("text to be replaced", "DeleteNotificationCorporateRecipient"), #"Link text <span>with further descriptive text</span>")
This approach has the advantage of not having to reproduce/understand the tag-rendering process.