Creating Generic View Components with Razor - asp.net-mvc

I want to write my own custom HTML helper that extends an existing helper. E.g. I want to create to extend #Html.EditorFor like so:
#Html.EditorFor(model => model.percent, new { data_a_sign="%", data_p_sign="s" })
Becomes:
#Html.PercentEditorFor(model => model.percent)
How would one go about writing that?
Something like this?
namespace AdminPortal.Helpers
{
public static class HtmlHelpers
{
public static MvcHtmlString PercentEditorFor<TModel>(this HtmlHelper html,
Expression<Func<TModel>> expression)
{
// Some Magic?
}
}
}
Any pointers would be greatly appreciated.

It's just a matter of returning the existing EditorFor method from your own helper:
public static MvcHtmlString PercentEditorFor<TModel>(this HtmlHelper html,
Expression<Func<TModel>> expression)
{
return html.EditorFor(...);
}
Put your own modified parameters into the EditorFor method. No magic required :)

Related

Creating a HtmlHelper to display content if certain conditions are fulfilled

I want to create a simple HtmlHelper to that I can use like this:
#using(Html.DisplayIf(Object object))
{
...
}
I tried the method suggested here, but unlike the guy who asked that question I would like the content between the brackets not to be rendered at all, not just hidden.
Is there a way to prevent the textwriter from writing the content between the brackets, or some other method that would be appropriate to solve my problem?
Edited
You can use the method described here: Capture wrapped content in BeginForm style disposable html helper.
I've applied the first method to your example.
public static class HtmlExtensions
{
public static HelperResult DisplayIf(this HtmlHelper html, Func<object, HelperResult> template, bool show)
{
return new HelperResult(writer =>
{
if (show)
{
template(null).WriteTo(writer);
}
});
}
}
You can call it like this:
#* Will render *#
#Html.DisplayIf(
#<span>test1</span>, true
)
#* Will not render *#
#Html.DisplayIf(
#<span>test2</span>, false
)

How to assign class for all Html.LabelFor

I have an app with about 50 of these:
#Html.LabelFor(m => m.CurrentLineItem.Address.Address1, new { #class = "textBox-label" })
It seems to me hardcoding a class is not a good idea. Is there a way to abstract this out further? Like maybe:
#Html.LabelWithTextBoxLabelClass(m=> m.CurrentLineItem.Address.Address1);
just like others said, make your own Html Helper, code like below:
public static class LabelExtensions
{
public static MvcHtmlString LabelWithTextBoxLabelClass(this HtmlHelper helper, string expression)
{
return helper.Label(expression, new { #class = "textBox-label" });
}
public static MvcHtmlString LabelForWithTextBoxLabelClass<TModel,TValue>(this HtmlHelper<TModel> helper, Expression<Func<TModel,TValue>> expression)
{
return helper.LabelFor(expression, new {#class = "textBox-label"});
}
}
now you can use it like what you want:
#Html.LabelWithTextBoxLabelClass(m=> m.CurrentLineItem.Address.Address1);
You can make your own Html helpers, or you can use Bootstrap Html Helpers for MVC. Take a look at here: http://www.codeproject.com/Articles/570762/TwitterBootstrapMvc
With bootstrap you can simply set your label class like this:
#Html.Bootstrap().LabelFor(m => m.Property).Class("textBox-label")

How to sort all dropdownLists by name in ASP.net MVC

Is it possible to automatically sort all dropdownLists in ASP.net MVC project?
I don't want to go one by one and sort them explicitly. Is there a way to do this automatically on all dropdownLists in the project?
Create a HtmlHelperExtensions class that has an extension method that does what you want. Something like this:
public static class HtmlHelperExtensions
{
public static MvcHtmlString SortedDropDownList(this HtmlHelper htmlHelper, string name, IEnumerable<SelectListItem> selectList)
{
return htmlHelper.DropDownList(name, selectList.OrderBy(x => x.Text));
}
}
Whatever namespace you stick the helper in, make sure it's added to configuration\system.web.webPages.razor\pages\namespaces in the web.config found in your \Views folder so that you can use it in your view.
You could create HtmlHelperExtension class as friism sugested, or you could create extension method on SelectList like this:
public static class SortedList
{
public static void SortList(this SelectList selectList, SortDirection direction)
{
//sort content of selectList
}
}
and then use it like this:
var sel = new SelectList(new List<string> {"john", "mary", "peter"});
sel.SortList(SortDirection.Ascending);
Either way, you are going to change every line of code where you want to sort those lists.

RouteLink in HtmlHelper?

How can I make up a RouteLink in a custom HtmlHelper? I know how to make it in a partial view but I want to build up a new link in a custom htmlhelper extension method with the use of a RouteLink. How to accomplish this?
Update: I noticed HtmlHelper.GenerateRouteLink. But what do I need to put in as parameters?
Here's an example. Let's suppose that you want to wrap the links into a div tag with some given class so that your resulting html looks like this:
<div class="foo">Some text</div>
You could write the following extension method:
public static class HtmlExtensions
{
public static MvcHtmlString CustomRouteLink(
this HtmlHelper htmlHelper,
string className,
string linkText,
object routeValues
)
{
var div = new TagBuilder("div");
div.MergeAttribute("class", className);
div.InnerHtml = htmlHelper.RouteLink(linkText, routeValues).ToHtmlString();
return MvcHtmlString.Create(div.ToString());
}
}
which could be used like this:
<%= Html.CustomRouteLink("foo", "Some text",
new { action = "index", controller = "home" }) %>
and this will produce the desired markup. Any other overloads of RouteLink could be used if necessary.
Once you get an instance of the UrlHelper you should be able to do whatever you want to do in your HtmlHelper method
UrlHelper url = new UrlHelper(helper.ViewContext.RequestContext);

can i change the way LabelFor render in MVC?

i would like to change the way LabelFor render. Can i do that with a DisplayTemplate?
LabelFor generate a label tag and i would like to add a ":" at the end of the label.
thank you!
alex
Here is an HTML Helper that will do that:
public static class LabelExtensions {
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
public static MvcHtmlString SmartLabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression) {
return LabelHelper(html,
ModelMetadata.FromLambdaExpression(expression, html.ViewData),
ExpressionHelper.GetExpressionText(expression));
}
internal static MvcHtmlString LabelHelper(HtmlHelper html, ModelMetadata metadata, string htmlFieldName) {
string labelText = metadata.DisplayName ?? metadata.PropertyName ?? htmlFieldName.Split('.').Last();
if (String.IsNullOrEmpty(labelText)) {
return MvcHtmlString.Empty;
}
// uncomment if want * for required field
//if (metadata.IsRequired) labelText = labelText + " *";
labelText = labelText + ":";
TagBuilder tag = new TagBuilder("label");
tag.Attributes.Add("for", html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(htmlFieldName));
tag.SetInnerText(labelText);
return MvcHtmlString.Create(tag.ToString(TagRenderMode.Normal));
}
}
To use it:
<%:Html.SmartLabelFor(m => m.FirstName)%>
It will render:
<label for="FirstName">First Name:</label>
Or if you uncomment the required field related *
<label for="FirstName">First Name *:</label>
Just write a regular <label> element in plain HTML:
<label>My Label:</label>
If you want to output the for="" attribute and accurately render the control's name then use this extension method:
using System;
using System.Linq.Expressions;
using System.Web.Mvc;
namespace MvcLibrary.Extensions
{
public static class HtmlExtensions
{
public static MvcHtmlString FieldIdFor<TModel, TValue>(
this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression)
{
string htmlFieldName = ExpressionHelper.GetExpressionText(expression);
string inputFieldId = html.ViewContext.ViewData.
TemplateInfo.GetFullHtmlFieldId(htmlFieldName);
return MvcHtmlString.Create(inputFieldId);
}
}
}
Then you can use in your view like so:
<label for="<%= Html.FieldIdFor(m => m.EmailAddress) %>">E-mail address:</label>
<%= Html.TextBoxFor(m => m.EmailAddress) %>
The other posts cover different approaches, they are all equally valid, which one you go for is matter of personal preference. I personally prefer writing the <label> as plain HTML as it gives designers more flexibility with changing markup, adding extra attributes such as CSS classes etc. Also I feel the label text is a view concern and shouldn't be decorated on the ViewModel class, but that's just my personal opinion/preference, I know some people here will disagree with me and that's fine :-)
You can create a String.ascx in DisplayTemplates folder and provide your own implementation. Refer to the Overriding Templates section of the following article.
http://bradwilson.typepad.com/blog/2009/10/aspnet-mvc-2-templates-part-1-introduction.html
You could do this using MVC 2 (if possible) if you pass a custom ViewModel to the view.
using System.ComponentModel;
public class PersonViewModel
{
public PersonViewModel(string name)
{
this.Name = name;
}
[DisplayName(".Display Anything You Like Here.")]
public string Name { get; set; }
I think the best approach would be writing your own helper method that renders what you like. You can overload the existing method or simply create a new method.

Resources