The TempData output is plain text and putting a div around it will leave a formatted but empty div on the screen if there is no TempData.
Is there a way to apply a class to it so that it only shows when the TempData item is set?
Other than writing the div code into the TempData, which seems like a horrible idea.
I would probably write a helper:
public static class HtmlExtensions
{
public static string Message(this HtmlHelper htmlHelper, string key)
{
var message = htmlHelper.ViewContext.TempData[key] as string;
if (string.IsNullOrEmpty(message))
{
return string.Empty;
}
var builder = new TagBuilder("div");
builder.SetInnerText(message);
return builder.ToString();
}
}
Which could be used like so:
<%= Html.Message("someKeyToLookInTempData") %>
Related
After years of working with WebForms I recently started the transition to MVC. I'm trying to create a plugable, lightweight content editing module but I've run into some problems.
The idea is simple: create a HtmlHelper named EditableSimpleHtml that can be used in a #using... { } syntax so that the following can be achieved in a razor view:
#using (Html.EditableSimpleHtml("MyKey"))
{
<h3>Test</h3>
<p>
1<br />
</p>
}
The value between the {...} is the default value for when no content can not be found in the data storage.
I've create a HtmlHelper. Below is a simplified version:
public static IDisposable EditableSimpleHtml(this HtmlHelper helper, string key)
{
// Get the content from the data storage using the key (I will not show the provider itself, its just a provider that will access a db)
var provider = ContentEditing.Provider;
string value = provider.GetValue(key);
if (value == null)
{
// No value found in the data storage for the supplied key, we have to use the default value from within the #using... { } statement
// Can I get that value here? I want to to store it initialy in the data storage
value = "..."; // How to get html from within the #using... { }?
}
return new ContentEditableHtmlString(helper, value);
}
public class ContentEditableHtmlString : IDisposable
{
private readonly HtmlHelper _helper;
public ContentEditableHtmlString(HtmlHelper helper, string value)
{
_helper = helper;
var builder = new TagBuilder("div");
var writer = _helper.ViewContext.Writer;
writer.Write(builder.ToString(TagRenderMode.StartTag));
writer.Write(value);
}
public void Dispose()
{
_helper.ViewContext.Writer.Write("</div>");
}
}
The problem is that I can't get the (default) content from within the #using... { } statement in the HtmlHelper, or at least I don't know how. I need it in case I want to store it to the database initially.
Second problem is that the value between the #using... { } statement will always be rendered. In the case when the content can be loaded from the data storage I want the default value to be replaced with the value from the data storage.
Is there a way to achieve this or did I start of on a completely wrong path?
You can not get the html within the #using{...} statement the way you are doing right now.
The closest thing you can do is use Templated Razor Delegates
public static HelperResult EditableSimpleHtml(this HtmlHelper helper, string key,
Func<string, HelperResult> template)
{
var templateResult = template(null);
//you have your value here that you can return directly
//or you can return HelperResult to write to the response directly
var templateResultHtml = templateResult.ToHtmlString();
return new HelperResult(writer =>
{
templateResult.WriteTo(writer);
});
}
And in your view:
#Html.EditableSimpleHtml("MyKey", #<text>
<h3>Test</h3>
<p>#DateTime.Now</p>
</text>)
Is it possible to customize the Html.ValidationMessageFor method so that it produces different HTML?
I want to do something similar to:
<div class="field-error-box">
<div class="top"></div>
<div class="mid"><p>This field is required.</p></div>
</div>
I am not sure if it's possible to use paragraph instead of default span, as it may make impossible for validation plugin to place error messages. But for div -s, thats easy - you could write custom html helper.
Something along these lines (may need further testing/coding). You will need to include the namespace of this static extension method in your view, or put this into System.Web.Mvc.Html directly.
public static class Validator
{
public static MvcHtmlString MyValidationMessageFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression)
{
TagBuilder containerDivBuilder = new TagBuilder("div");
containerDivBuilder.AddCssClass("field-error-box");
TagBuilder topDivBuilder = new TagBuilder("div");
topDivBuilder.AddCssClass("top");
TagBuilder midDivBuilder = new TagBuilder("div");
midDivBuilder.AddCssClass("mid");
midDivBuilder.InnerHtml = helper.ValidationMessageFor(expression).ToString();
containerDivBuilder.InnerHtml += topDivBuilder.ToString(TagRenderMode.Normal);
containerDivBuilder.InnerHtml += midDivBuilder.ToString(TagRenderMode.Normal);
return MvcHtmlString.Create(containerDivBuilder.ToString(TagRenderMode.Normal));
}
}
As you see, this uses default ValidationMessageFor method, to not interfere with validation-plugin error message processing.
And you use this simply, as default validation message helper
#Html.MyValidationMessageFor(model => model.SomeRequiredField)
I used another way:
public static MvcHtmlString DivValidationMessageFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
{
return MvcHtmlString.Create(htmlHelper.ValidationMessageFor(expression).ToString().Replace("span", "div"));
}
This way you can use the built in way, but replace the span with a div.
If you need any other overloads of the function, just duplicate as necessary.
You can implement your own ValidationMessageFor helper to emit your desired output or use some javascript to add/modify the rendered HTML code but the custom ValidationMessageFor implementation is the cleaner approach IMHO.
To implement your own ValidationMessageFor helper take a look at the ValidationExtensions.ValidationMessageFor and ValidationMessageHelper methods in the ASP.NET MVC source code.
Implementation Hints
Since GetFormContextForClientValidation is internal you have to work around that implementation by duplicating the internal functionality in your code:
FormContext formContext = htmlHelper.ViewContext.ClientValidationEnabled ? htmlHelper.ViewContext.FormContext : null;
Some other methods are private in ValidationExtensions like GetUserErrorMessageOrDefault you would need to duplicate that code too. What you can do to avoid duplicating code is to let ValidationExtentensions.ValidationMessageFor render the validation message string that is wrapped in a span and afterwards change the rendered string according to your requirements. Keep in mind that "null" is returned in case no error was found and that you'll need the data- HTML attributes in case you have unobtrusive JavaScript enabled.
You can download the ASP.NET MVC 3 source code from here
The only need for change of the default tag generation was in my case, that spans behavior results in anoying margin setups.
I resolved this by using 'display: block'
Maybe this helps some people..
Maybe you can put that code
string propertyName = ExpressionHelper.GetExpressionText(expression);
string name = helper.AttributeEncode(helper.ViewData.TemplateInfo.GetFullHtmlFieldName(propertyName));
if (helper.ViewData.ModelState[name] == null ||
helper.ViewData.ModelState[name].Errors == null ||
helper.ViewData.ModelState[name].Errors.Count == 0)
{
return MvcHtmlString.Empty;
}
on top of the answered function, so that the div doesn't appear on the form load.
I created ValidationMessageAsStringFor which just returns the error message as string. It is basically a simplified version of ValidationMessageFor:
public static MvcHtmlString ValidationMessageAsStringFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression)
{
var field = ExpressionHelper.GetExpressionText(expression);
string modelName = helper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(field);
if (!helper.ViewData.ModelState.ContainsKey(modelName))
{
return null;
}
var modelState = helper.ViewData.ModelState[modelName];
var modelErrors = (modelState == null) ? null : modelState.Errors;
var modelError = ((modelErrors == null) || (modelErrors.Count == 0)) ? null : modelErrors.FirstOrDefault(m => !String.IsNullOrEmpty(m.ErrorMessage)) ?? modelErrors[0];
if (modelError == null)
{
return null;
}
var errorMessage = GetUserErrorMessageOrDefault(helper.ViewContext.HttpContext, modelError, modelState);
return MvcHtmlString.Create(errorMessage);
}
private static string GetUserErrorMessageOrDefault(HttpContextBase httpContext, ModelError error, ModelState modelState)
{
if (!string.IsNullOrEmpty(error.ErrorMessage))
{
return error.ErrorMessage;
}
if (modelState == null)
{
return null;
}
return modelState.Value?.AttemptedValue;
}
With this in place and after importing the namespace containing the new helper, just create the HTML code you need:
<div class="field-error-box">
<div class="top"></div>
<div class="mid"><p>#Html.ValidationMessageAsStringFor(m => m.FieldName)</p></div>
</div>
Yes, just use a metamodel for the field:
[MetadataType(typeof(YourMetaData))]
public partial class YOURCLASS
{
[Bind(Exclude = "objID")]
public class YourMetaData
{
[Required(AllowEmptyStrings = false, ErrorMessage = "Please enter a name")]
public object Name { get; set; }
}
}
Change your message at the ErrorMessage field :)
Hope this help :)
I have a label as
<%= Html.Label("");%>
i want to add content to label at runtime, unfortunately it doesn't take any other parameter to create id property for it. can't i create id property for it just similar to asp:label
thanks,
michaeld
No need to use the HtmlHelper functions always and everywhere if they don't fit your need. They're just supposed to make your life easier, not harder. Use good ole HTML here:
<label id="id_for_label"></label>
If you want to keep using HtmlHelper functions you can always create your own extension methods.
For example:
public static class LabelHelper
{
private static string HtmlAttributes(object htmlAttributes)
{
var builder = new StringBuilder();
foreach (PropertyDescriptor descriptor in
TypeDescriptor.GetProperties(htmlAttributes))
{
builder.AppendFormat(" {0}=\"{1}\" ", descriptor.Name,
descriptor.GetValue(htmlAttributes));
}
return builder.ToString();
}
public static MvcHtmlString MyLabel(this HtmlHelper htmlHelper,
string labelText, object htmlAttributes)
{
var attributes = HtmlAttributes(htmlAttributes);
return MvcHtmlString.Create(
String.Format("<label for=\"{0}\" {1}>{0}</label",
labelText, attributes.Trim()));
}
}
Then you can add a label to a view in the following manner:
<%: Html.MyLabel("Hello, World!", new { #id = "myLabel" })%>
The generated HTML is:
<label for="Hello, World!" id="myLabel">Hello, World!</label>
For MVC 3 such a helper function is already available:
http://msdn.microsoft.com/en-us/library/gg538318(v=VS.99).aspx
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.
I wish to display content depending on the given role(s) of the active user , in the ASP.NET MVC.
Compare the old fashion way, using WebForms:
protected void Page_Load(Object sender, EventArgs e)
{
if(User.IsInRole("Administrator")) {
adminLink.Visible = true;
}
}
Now how would I go on writing that when using the ASP.NET MVC ?
From my point of view, it would be wrong to place it directly in the View File, and assigning a variable for every single view won't be pretty either.
Create Html helper and check current user roles in its code:
public static class Html
{
public static string Admin(this HtmlHelper html)
{
var user = html.ViewContext.HttpContext.User;
if (!user.IsInRole("Administrator")) {
// display nothing
return String.Empty;
// or maybe another link ?
}
var a = new TagBuilder("a");
a["href"] = "#";
a.SetInnerText("Admin");
var div = new TagBuilder("div") {
InnerHtml = a.ToString(TagRenderMode.Normal);
}
return div.ToString(TagRenderMode.Normal);
}
}
UPDATED:
Or create wrapper for stock Html helper. Example for ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName):
public static class Html
{
public static string RoleActionLink(this HtmlHelper html, string role, string linkText, string actionName, string controllerName)
{
return html.ViewContext.HttpContext.User.IsInRole(role)
? html.ActionLink(linkText, actionName, controllerName)
: String.Empty;
}
}
No you would be placing it in the view file, like so actually:
<% If (User.IsInRole("Administrator")) { %>
<div>Admin text</div>
<% } %>
this worked for me:
<% MembershipUser mu = Membership.GetUser();
if (mu != null)
if (Roles.IsUserInRole(mu.UserName, "Administrator"))
{
%>
<li class="paddingleftThree"><%= Html.ActionLink("User Administration", "GetUsers", "Account")%></li> <%} %>
The separation of concerns approach suggested in ASP.NET MVC 4 How do you serve different HTML based on Role? in my opinion is a better way to go.
Personally I avoid IsInRole check as much as possible in the code and leave it to declarative means to achieve role based restriction as much as possible. This ensures code remains maintainable over time. I am not sure if this is a right or the wrong approach, but has worked well for me.