Set optional disabled attribute - html

I want to disable all fields in my form, which have values when page is loaded.
For example in this
<td>#Html.TextBoxFor(m => m.PracticeName, new { style = "width:100%", disabled = Model.PracticeName == String.Empty ? "Something Here" : "disabled" })</td>
I want to write inline something like this. I don't want to use if-else and make my code larger.
Using javascript/jquery doesn't welcome too.
I tried to write false/true, but 1.It maybe isn't cross-browser 2.Mvc parsed it to string like "True" and "False".
So how can I do it?
P.S. I use ASP.NET MVC 3 :)

Seems like a good candidate for a custom helper:
public static class HtmlExtensions
{
public static IHtmlString TextBoxFor<TModel, TProperty>(
this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> ex,
object htmlAttributes,
bool disabled
)
{
var attributes = new RouteValueDictionary(htmlAttributes);
if (disabled)
{
attributes["disabled"] = "disabled";
}
return htmlHelper.TextBoxFor(ex, attributes);
}
}
which could be used like this:
#Html.TextBoxFor(
m => m.PracticeName,
new { style = "width:100%" },
Model.PracticeName != String.Empty
)
The helper could obviously be taken a step further so that you don't need to pass an additional boolean value but it automatically determines whether the value of the expression is equal to default(TProperty) and it applies the disabled attribute.
Another possibility is an extension method like this:
public static class AttributesExtensions
{
public static RouteValueDictionary DisabledIf(
this object htmlAttributes,
bool disabled
)
{
var attributes = new RouteValueDictionary(htmlAttributes);
if (disabled)
{
attributes["disabled"] = "disabled";
}
return attributes;
}
}
which you would use with the standard TextBoxFor helper:
#Html.TextBoxFor(
m => m.PracticeName,
new { style = "width:100%" }.DisabledIf(Model.PracticeName != string.Empty)
)

I used Darin's answer. However, when it came to data_my_custom_attribute, they were not rendered as data-my-custom-attribute. So I changed Darin's code to handle this.
public static RouteValueDictionary DisabledIf(
this object htmlAttributes,
bool disabled
)
{
RouteValueDictionary htmlAttributesDictionary
= HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
if (disabled)
{
htmlAttributesDictionary["disabled"] = "disabled";
}
return new RouteValueDictionary(htmlAttributesDictionary);
}

Related

How to include CSS style inside HTML style (code generated via ASP.Net MVC Razor page)

I'm still at my early beginning with ASP.Net MVC. So, I'm not sure if I'm following good practices or not.
I want to generate HTML code based on CSS style
Razor page:
#foreach (p in ...)
{
<td style="#Helper.CustomBackground(p) #Helper.CustomComplexStyle(p)">...</td>
}
CSS classes:
.complexStyleA {
// ...
}
.complexStyleB { }
C# helper method:
// None of the styling method has the "style" attribute, so I can concatenate several custom-styles
public static class Helper
{
// Basic direct styling
public static string CustomBackground(object p) => "{ background: red; }";
// More complex styling based on CSS ones
public static string CustomComplexStyle(object p) => "{ // complexStyleA, complexStyleB, etc. based on p; }"
}
I don't really mind generated all the code inside the HTML tags, but generate a table with several thousand lines and can obviously reduce the file size!
Thanks for any insights :-)
UPDATE
I think that I will do something as follow :
<td #Html.Raw(#Hepers.ConcatenateCSSClasses(#CustomComplexStyle(p),
#OtherCustomComplexStyle(p),
...))>
...
</td>
where #concatenateCSSClasses() removes all " class=" ans #...Syle(p) returns "class=\"...StyleXYZ\"", unless I can have duplicated tag attributes.. Any better ideas?
UPDATE with cleaner methods
public static string HTMLTagAttribute(FieldType fieldType, Field field = null)
{
string output = "";
// Makes distinction between hearder and body
if (field == null)
{
output += HTMLHeaderTagClassAttributes(fieldType);
output += HTMLHeaderTagStyleAttributes(fieldType);
}
else
{
output += HTMLBodyTagClassAttributes(fieldType, field);
output += HTMLBodyTagStyleAttributes(fieldType, field);
}
return output;
}
Body only shown with clear distinction between classes and (inline styles). I don't detail the samll method HTMLWidth(), HTMLBackground(), etc. which are self explanatory.
#region HTML Body Tag Attribute Methods
private static string HTMLBodyTagStyleAttributes(FieldType fieldType, Field field)
{
AttributeValues style = new AttributeValues("style");
style += HTMLWidth(fieldType);
style += HTMLBackground(fieldType, field);
style += HTMLAlign(fieldType);
return style.ToString();
}
private static string HTMLBodyTagClassAttributes(FieldType fieldType, Field field)
{
AttributeValues cls = new AttributeValues("class");
cls += HTMLTopBorder(fieldType, field);
cls += HTMLSideBorder(fieldType);
return cls.ToString();
}
#endregion
Class which helps concatenate class and style attribute values (with no extra space).
/// <summary>Class to help format HMTL class and style attribute values</summary>
class AttributeValues
{
private readonly string _attribute = "";
private readonly List<string> _values = new List<string>();
// TODO - Make distinction bewteen class and styles
#region Constructors
public AttributeValues(string attribute)
{
_attribute = attribute;
}
#endregion
public static AttributeValues operator +(AttributeValues values, string value)
{
if (value != "") values._values.Add(value);
return values;
}
public static bool operator ==(AttributeValues values, string value) => values == value;
public static bool operator !=(AttributeValues values, string value) => values != value;
#region Public Overridden Methods
public override string ToString()
{
string values = "";
foreach (string value in _values)
{
if (values != "") values += " ";
values += value;
}
return values != "" ? $"{ _attribute }=\"{ values }\"" :"" ;
}
public override bool Equals(object obj) => base.Equals(obj);
public override int GetHashCode() => base.GetHashCode();
#endregion
}
And finally in the Razor page (apparently, I have to wrap it in HTML.Raw() because of the " (I agree that it could be refractor within the th, but doesn't make the code easier):
<td Html.Raw(Helpers.HTMLTagAttribute(Enums.FieldType.Account))>..</td>

Adding a dynamic list of parameters with Html helper

I'd like to be able to feed in a list of parameter names and values into a Html.Actionlink but the helper doesn't create the parameters as I would like. Any ideas how to do this?
public class ParameterNameValue
{
public string ParameterName { get; set; }
public string ParameterValue { get; set; }
}
View
#foreach (var action in post.FeedActions)
{
var parameters = "";
foreach (var param in action.Parameters)
{
parameters += param.ParameterName + "=" + param.ParameterValue + ",";
}
#Html.ActionLink(#action.Label, action.ActionName,
new { controller = action.Controller, id = action.CommunityId, slug = action.Slug,
Fromfeed=true,parameters }, new { #class = action.Classes })
}
yields a link like this:
Whereas I need the parameters part to look like:
?FromFeed=true&MatchId=1234&InnerId=5678
edit: I got it working by just manually creating the tag, but no doubt there's a nice way of doing this by creating a custom helper.
#action.Label
I'd suggest you to extend the classic ActionLink helper with a prototype similar to this (add a parameter for your specific class) :
public static MvcHtmlString ActionLinkCustom(this HtmlHelper html, string linkText, string actionName, string controllerName, object routeValues, List<ParameterNameValue> yourOtherValues)
In the code, check if you got any custom values. If such, add them to the RouteValuesDictionnary. Then use the classic ActionLink helper providing this modified RouteValuesDictionnary.
Note : you can work on the routeValues using this
IDictionary<string, object> RouteValues = HtmlHelper.ObjectToDictionary(routeValues);

Razor syntax - how to conditionally wrap some inner HTML

In Razor, there's a curious rule about only allowing closed HTML within an if block.
See:
Razor doesn't understand unclosed html tags
But I have a situation where I want to exclude some outer, wrapping elements under certain conditions. I don't want to repeat all the inner HTML, which is a fair amount of HTML and logic.
Is the only way around the problem to make yet another partial view for the inner stuff to keep it DRY?
Without any other re-use for this new partial, it feels really awkward, bloaty. I wonder if the rule is a limitation of Razor or simply a nannying (annoying) feature.
You can use Html.Raw(mystring). In myString you can write whatever you want, for example a tag opening or closing, without getting any errors at all. I.e.
if (condition) {
#Html.Raw("<div>")
}
if (condition) {
#Html.Raw("</div>")
}
NOTE: the #Html.Raw("<div>") can be written in a shorter, alternative form, like this: #:<div>
You can also create your own html helpers for simplifying the razor syntax. These helpers could receive the condition parameter, so that you can do something like this:
#Html.DivOpen(condition)
#Html.DivClose(condition)
or more complicated helpers that allow to specify attributes, tag name, and so on.
Nor the Raw, neither the html helpers will be detected as "tags" so you can use them freely.
The Best Way to do it
It would be much safer to implement something like the BeginForm html helper. You can look at the source code. It's easy to implement: you simply have to write the opening tag in the constructor, and the closing tag in the Dispose method. The adavantage of this technique is that you will not forget to close a conditionally opened tag.
You need to implement this html helper (an extension method declared in a static class):
public static ConditionalDiv BeginConditionalDiv(this HtmlHelper html,
bool condition)
{
ConditionalDiv cd = new ConditionalDiv(html, condition);
if (condition) { cd.WriteStart(); }
return cd; // The disposing will conditionally call the WriteEnd()
}
Which uses a class like this:
public class ConditionalDiv : IDisposable
{
private HtmlHelper Html;
private bool _disposed;
private TagBuilder Div;
private bool Condition;
public ConditionalDiv(HtmlHelper html, bool condition)
{
Html = html;
Condition = condition;
Div = new TagBuilder("div");
}
public void Dispose()
{
Dispose(true /* disposing */);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
_disposed = true;
if (Condition) { WriteEnd(); }
}
}
public void WriteStart()
{
Html.ViewContext.Writer.Write(Div.ToString(TagRenderMode.StartTag));
}
private void WriteEnd()
{
Html.ViewContext.Writer.Write(Div.ToString(TagRenderMode.EndTag));
}
}
You can use this with the same pattern as BeginForm. (Disclaimer: this code is not fully tested, but gives an idea of how it works. You can accept extra parameters for attributes, tag names and so on).
Declare razor helper using #helper HeplerName(), call by #HeplerName() anywhere. And you can add params if you want.
#if (condition)
{
<div class="wrapper">
#MyContent()
</div>
}
else
{
#MyContent()
}
#helper MyContent()
{
<img src="/img1.jpg" />
}
Edit (thanks to #Kolazomai for bringing up the point in comments):
ASP.NET Core 3.0 no longer supports #helper but supports HTML markup in method body. New case will look like this:
#if (condition)
{
<div class="wrapper">
#{ MyContent(); }
</div>
}
else
{
#{ MyContent(); }
}
#{
void MyContent()
{
<img src="/img1.jpg" />
}
}
The following code is based on the answer of #JotaBe, but simplified and extendable:
public static class HtmlHelperExtensions
{
public static IDisposable BeginTag(this HtmlHelper htmlHelper, string tagName, bool condition = true, object htmlAttributes = null)
{
return condition ? new DisposableTagBuilder(tagName, htmlHelper.ViewContext, htmlAttributes) : null;
}
}
public class DisposableTagBuilder : TagBuilder, IDisposable
{
protected readonly ViewContext viewContext;
private bool disposed;
public DisposableTagBuilder(string tagName, ViewContext viewContext, object htmlAttributes = null) : base(tagName)
{
this.viewContext = viewContext;
if (htmlAttributes != null)
{
this.MergeAttributes(HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
}
this.Begin();
}
protected virtual void Begin()
{
this.viewContext.Writer.Write(this.ToString(TagRenderMode.StartTag));
}
protected virtual void End()
{
this.viewContext.Writer.Write(this.ToString(TagRenderMode.EndTag));
}
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
this.disposed = true;
this.End();
}
}
}
This can be used the following way within Razor views:
#using (Html.BeginTag("div"))
{
<p>This paragraph is rendered within a div</p>
}
#using (Html.BeginTag("div", false))
{
<p>This paragraph is rendered without the div</p>
}
#using (Html.BeginTag("a", htmlAttributes: new { href = "#", #class = "button" }))
{
<span>And a lot more is possible!</span>
}
This is IMO the most convenient way to achieve this:
[HtmlTargetElement(Attributes = RenderAttributeName)]
public class RenderTagHelper : TagHelper
{
private const string RenderAttributeName = "render";
private const string IncludeContentAttributeName = "include-content";
[HtmlAttributeName(RenderAttributeName)]
public bool Render { get; set; }
[HtmlAttributeName(IncludeContentAttributeName)]
public bool IncludeContent { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
if (!Render)
{
if (IncludeContent)
output.SuppressOutput();
else
output.TagName = null;
}
output.Attributes.RemoveAll(RenderAttributeName);
output.Attributes.RemoveAll(IncludeContentAttributeName);
}
}
Then, you just use it like this:
<div render="[bool-value]" include-content="[bool-value]">
...
</div>

How to add an extra property into a serialized JSON string using json.net?

I am using Json.net in my MVC 4 program.
I have an object item of class Item.
I did:
string j = JsonConvert.SerializeObject(item);
Now I want to add an extra property, like "feeClass" : "A" into j.
How can I use Json.net to achieve this?
You have a few options.
The easiest way, as #Manvik suggested, is simply to add another property to your class and set its value prior to serializing.
If you don't want to do that, the next easiest way is to load your object into a JObject, append the new property value, then write out the JSON from there. Here is a simple example:
class Item
{
public int ID { get; set; }
public string Name { get; set; }
}
class Program
{
static void Main(string[] args)
{
Item item = new Item { ID = 1234, Name = "FooBar" };
JObject jo = JObject.FromObject(item);
jo.Add("feeClass", "A");
string json = jo.ToString();
Console.WriteLine(json);
}
}
Here is the output of the above:
{
"ID": 1234,
"Name": "FooBar",
"feeClass": "A"
}
Another possibility is to create a custom JsonConverter for your Item class and use that during serialization. A JsonConverter allows you to have complete control over what gets written during the serialization process for a particular class. You can add properties, suppress properties, or even write out a different structure if you want. For this particular situation, I think it is probably overkill, but it is another option.
Following is the cleanest way I could implement this
dynamic obj = JsonConvert.DeserializeObject(jsonstring);
obj.NewProperty = "value";
var payload = JsonConvert.SerializeObject(obj);
You could use ExpandoObject.
Deserialize to that, add your property, and serialize back.
Pseudocode:
Expando obj = JsonConvert.Deserializeobject<Expando>(jsonstring);
obj.AddeProp = "somevalue";
string addedPropString = JsonConvert.Serializeobject(obj);
I think the most efficient way to serialize a property that doesn't exist in the type is to use a custom contract resolver. This avoids littering your class with the property you don't want, and also avoids the performance hit of the extra serialization round trip that most of the other options on this page incur.
public class SpecialItemContractResolver : DefaultContractResolver {
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization) {
var list = base.CreateProperties(type, memberSerialization);
if (type.Equals(typeof(Item))) {
var feeClassProperty = CreateFeeClassProperty();
list.Add(feeClassProperty);
}
return list;
}
private JsonProperty CreateFeeClassProperty() {
return new JsonProperty {
PropertyName = "feeClass",
PropertyType = typeof(string),
DeclaringType = typeof(Item),
ValueProvider = new FeeClassValueProvider(),
AttributeProvider = null,
Readable = true,
Writable = false,
ShouldSerialize = _ => true
};
}
private class FeeClassValueProvider : IValueProvider {
public object GetValue(object target) => "A";
public void SetValue(object target, object value) { }
}
}
To use this functionality:
// This could be put in a static readonly place so it's reused
var serializerSettings = new JsonSerializerSettings {
ContractResolver = new SpecialItemContractResolver()
};
// And then to serialize:
var item = new Item();
var json = JsonConvert.Serialize(item, serializerSettings);

How to disable input field's autocomplete with EditorFor?

<%= Html.EditorFor(product => product.Name) %>
I need the generated output to have autocomplete="off" attribute set.
What I'm missing?
Edit:
I'm looking an extension method for EditorFor that accepts key/value dictionary for attributes, so I can call it like this: <%= Html.EditorFor(product => product.Name, new { autocomplete = "off" } ) %>
Here it is done for LabelFor, but it is needed to be adjusted for EditorFor
public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, object htmlAttributes) {
return LabelFor(html, expression, new RouteValueDictionary(htmlAttributes));
}
public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, IDictionary<string, object> htmlAttributes)
{
ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
string htmlFieldName = ExpressionHelper.GetExpressionText(expression);
string labelText = metadata.DisplayName ?? metadata.PropertyName ?? htmlFieldName.Split('.').Last();
if (String.IsNullOrEmpty(labelText))
{
return MvcHtmlString.Empty;
}
TagBuilder tag = new TagBuilder("label");
tag.MergeAttributes(htmlAttributes);
tag.Attributes.Add("for", html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(htmlFieldName));
tag.SetInnerText(labelText);
return MvcHtmlString.Create(tag.ToString(TagRenderMode.Normal));
}
Edit2:
I realized it can't be named EditorFor because there already exists an overridden EditorFor that accepts an anonymous type, see here http://msdn.microsoft.com/en-us/library/ff406462.aspx.. anyway, we can name it differently, no biggies.
You'll need to use use a custom template that generates the input element with the attribute or you could add some javascript to the page to add the attribute client-side.
<%= Html.EditorFor( product => product.Name, "NoAutocompleteTextBox" ) %>
Then in Shared/EditorTemplates you need a NoAutocompleteTextBox.ascx that defines
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<%= Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue,
new { autocomplete = "off" }) %>
or, the jQuery way, to set it on all text inputs
$(function() {
$('input[type=text]').attr('autocomplete','off');
});
public static MvcHtmlString EditorForAttr<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, object htmlAttributes) {
return EditorForAttr(html, expression, new RouteValueDictionary(htmlAttributes));
}
public static MvcHtmlString EditorForAttr<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, IDictionary<string, object> htmlAttributes) {
ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
string htmlFieldName = ExpressionHelper.GetExpressionText(expression);
TagBuilder tag = new TagBuilder("input");
tag.GenerateId(html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(htmlFieldName));
tag.MergeAttribute("name", htmlFieldName);
tag.MergeAttribute("type", "text");
tag.MergeAttribute("value", metadata.Model == null ? "" : metadata.Model.ToString()); // Not sure if this is correct.
tag.MergeAttributes(htmlAttributes, true);
return MvcHtmlString.Create(tag.ToString(TagRenderMode.SelfClosing));
}