I'm trying to make some fields editable and some fields read-only based on user permissions (MVC version 5.2.3). I can see a whole bunch of answers on this subject for the #html.EditorFor(), but not plain old #html.Editor(). I've tried the following, none of which have yielded read-only fields:
#Html.Editor(property.Name, new { #disabled = "true" })
#Html.Editor(property.Name, new { #disabled = "disabled" })
Can anyone help me? Thanks very much.
You can write your own extension to receive your boolean property "disabled" as a parameter :
public static class HtmlExtensions
{
public static MvcHtmlString EditorDisabled<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, bool disabled, object htmlAttributes = null)
{
return EditorDisabled(htmlHelper, expression, disabled, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
}
public static MvcHtmlString EditorDisabled<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, bool disabled, IDictionary<string, object> htmlAttributes)
{
if (htmlAttributes == null)
htmlAttributes = new Dictionary<string, object>();
if (disabled)
htmlAttributes["disabled"] = "disabled";
return htmlHelper.Editor(expression, htmlAttributes);
}
}
You could just change it from Editor to:
#Html.DisplayFor(model => model.Title)
And it will just pull the value from your model. From there you will just need to implement logic in razor regarding who can see what. EX:
if(Request.IsAuthenticated){
if (isAdmin){
#Html.Editor(model => model.Title)
}
if (isUser){
#Html.DisplayFor(model => model.Title)
}
}
I hope this helps put you on the right path!
Related
I am trying to create a custom Html helper with ASP.NET MVC. I have the following code:
#helper DefaultRenderer(Models.Control control)
{
<div class="form-group">
<label class="control-label" for="#control.Name">#control.Label</label>
#Html.TextBoxFor(m => control.Value, new { #class = "form-control" })
</div>
}
Apparently #Html.TextBoxFor cannot be found inside a Helper .cshtml class. I can use it in a partial view which is also a .cshtml class.
I can use #HtmlTextBox but then I will lose the strong model binding...
Why is this happening and is there a way to get it to work?
No, this is not possible. You could not write a normal HTML helper with #Html.TextBoxFor because that your view is strongly typed.
So you need something like:
public class HelperExtentions{
public static MvcHtmlString DefaultRenderer<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, Control control , object htmlAttributes)
{
var sb = new StringBuilder();
var dtp = htmlHelper.TextBoxFor(expression, htmlAttributes).ToHtmlString();
sb.AppendFormat("<div class='form-group'><label class='control-label' for='{1}'>{2}</label>{0}</div>", dtp,control.Name,control.Label);
return MvcHtmlString.Create(sb.ToString());
}
}
Then you can use :
#html.DefaultRenderer(m => m.Control.Value, Models.Control,new { #class = "form-control" }
I have finished my asp.net MVC web application, and I have been using the data annotation [Required] to mention that the field is required. But currently the required fields does not have any indication that they are required, unless the user tried to submit the form. So is there way to force my Razor view to display a red “” beside any field that have [Required] defined on it? OR I need to manually add the “” icon ?
Thanks
After I got burned by the Bootstrap 2 to 3 upgrade, where they pretty much completely changed the HTML for form controls, I've been putting the entire form group in editor templates instead of just the field. Here's an example:
<div class="form-group">
#Html.Label("", new { #class = string.Format("control-label col-md-2{0}", ViewData.ModelMetadata.IsRequired ? string.Empty : " optional") })
<div class="col-md-6">
#Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue.ToString(), new { type = "email", #class = "form-control" })
#Html.ValidationMessage("")
</div>
</div>
What's important here for you is the string.Format in Html.Label. I'm using the ViewData.ModelMetadata.IsRequired to add an "optional" class if it's false. Bootstrap makes the labels bold by default, so as a required indicator, I make optional field labels normal (non-bold). However, adding a * is a little more difficult. You could use the same thing I'm doing here to add an additional span tag:
#if (ViewData.ModelMetadata.IsRequired)
{
<span class="requiredIndicator>*</span>
}
#Html.Label(...)
...
The potential problem is that that won't actually be inside your <label> tag, so you might have to do some extra styling work to make it look right depending on the styles you apply to the labels.
An alternative is to create your own HtmlHelper to return a label with a required indicator. Here's some sample code for that:
public static MvcHtmlString RequiredIndicatorLabelFor<TModel, TValue>(
this HtmlHelper<TModel> html,
Expression<Func<TModel, TValue>> modelProperty,
object htmlAttributes)
{
var metadata = ModelMetadata.FromLambdaExpression(modelProperty, html.ViewData);
var labelText = metadata.IsRequired ? string.Format("* {0}", metadata.GetDisplayName()) : metadata.GetDisplayName();
return html.LabelFor(modelProperty, labelText, htmlAttributes);
}
You can also write a custom label helper for this purpose
public static MvcHtmlString CustomLabelFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression,
IDictionary<string, object> htmlAttributes = null )
{
var metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
var propertyName = ExpressionHelper.GetExpressionText(expression);
var builder = new TagBuilder("label");
builder.Attributes.Add("for", TagBuilder.CreateSanitizedId(htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(propertyName)));
var labelText = string.Format("{0}{1}", metadata.IsRequired ? "*" : string.Empty,
string.IsNullOrEmpty(metadata.DisplayName)
? metadata.PropertyName
: metadata.DisplayName);
builder.SetInnerText(labelText);
builder.MergeAttributes<string, object>(htmlAttributes, true);
return new MvcHtmlString(builder.ToString());
}
Now when used CustomLabelFor on a property with Required attribute, it will append * in fort of the label text.
#Html.CustomLabelFor(m => m.YourRequiredField)
I have a mvc5 webpage that has a form method that when the checkbox is checked submits the form and sends the get request. The request header submits the CarNumber and CarId as expected but the checkbox value is sent twice in the header why is this happening? How can I fix the issue.
Header value repeated like this
ShowAllDesignReviews=true&ShowAllDesignReviews=false
#using (Html.BeginForm("Index", "DesignReview", FormMethod.Get))
{
#Html.Hidden("CarNumber", "CarNumber")
#Html.Hidden("CarId", "CarId")
#Html.CheckBoxFor(model => Model.ShowAllDesignReviews, new { #onchange = "this.form.submit()" })
}
This is how CheckBoxFor helper in MVC generates the html, for easy model binding
<input name="ShowAllDesignReviews" type="checkbox" value="true" />
<input name="ShowAllDesignReviews" type="hidden" value="false" />
If you dont select check box, the field will not be posted. To make the value(false) to be passed they used Hidden.
Refer asp.net mvc: why is Html.CheckBox generating an additional hidden input
Better to have your controller that accepts the model
public ActionResult Save(Design model)
{
var boolSet=model.ShowAllDesignReviews;
}
See Murali's answer as to why this happens.
I had some similar scenarios where I wanted to have a "standard" checkbox, so I wrote the following HtmlHelper:
public static class StandardCheckboxForHelper
{
public static MvcHtmlString StandardCheckboxFor<TModel, TProperty>(this HtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> propertyExpression)
{
return html.StandardCheckboxFor(propertyExpression, null);
}
public static MvcHtmlString StandardCheckboxFor<TModel, TProperty>(this HtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> propertyExpression, object htmlAttributes)
{
return html.StandardCheckboxFor(propertyExpression, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
}
public static MvcHtmlString StandardCheckboxFor<TModel, TProperty>(this HtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> propertyExpression, IDictionary<string, object> htmlAttributes)
{
var propertyName = html.NameFor(propertyExpression).ToString();
var propertyId = html.IdFor(propertyExpression).ToString();
var propertyValueString = html.ValueFor(propertyExpression).ToString();
var propertyValue = ModelMetadata.FromLambdaExpression(propertyExpression, html.ViewData).Model;
var unobtrusiveValidationAttributes = html.GetUnobtrusiveValidationAttributes(propertyName, ModelMetadata.FromLambdaExpression(propertyExpression, html.ViewData));
var checkbox = new TagBuilder("input");
checkbox.MergeAttribute("type", "checkbox");
checkbox.MergeAttribute("id", propertyId);
checkbox.MergeAttribute("name", propertyName);
checkbox.MergeAttributes(unobtrusiveValidationAttributes);
checkbox.MergeAttributes(htmlAttributes);
bool propertyValueConverted;
if ((propertyValue is bool) && (bool) propertyValue)
checkbox.MergeAttribute("checked", "checked");
else if ((propertyValue is bool?) && ((bool?)propertyValue).GetValueOrDefault(false))
checkbox.MergeAttribute("checked", "checked");
else if (bool.TryParse(propertyValueString, out propertyValueConverted) && propertyValueConverted)
checkbox.MergeAttribute("checked", "checked");
return checkbox.ToHtml();
}
}
Usage is the same as the CheckboxFor helper, and it will only render one simple checkbox.
I have Html helper method, creating a check box along with some text, I pass.
#Html.CheckBoxFor(x => x.SomeProperty,#<text> <ul> <li> </li> </ul> </text>}))
public static MvcHtmlString CheckBoxFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, bool>> expression, ?? )
{
var chkBox = helper.CheckBoxFor(expression).ToString();
return MvcHtmlString.Create(string.Format("<li>{0}</li><div>{1}</div>", chkBox, ??);
}
What would be the signature of my method then. Some lambda/expression or something.
Help will be appreciated.
Regards
Parminder
I would recommend you looking at templated razor delegates. So in your case the helper might look something along the lines of:
public static MvcHtmlString CheckBoxFor<TModel>(
this HtmlHelper<TModel> helper,
Expression<Func<TModel, bool>> expression,
Func<object, HelperResult> template)
{
var chkBox = helper.CheckBoxFor(expression).ToHtmlString();
return MvcHtmlString.Create(
string.Format("<li>{0}</li><div>{1}</div>", chkBox, template(null))
);
}
and in the view:
#model MyViewModel
#using (Html.BeginForm())
{
#Html.CheckBoxFor(
x => x.SomeProperty,
#<text><ul><li></li></ul></text>
)
<input type="submit" value="OK" />
}
Just string. It's going into string.Format, so that gives you a hint.
This code:
#Html.HiddenFor(model => model.Country.CountryId)
gives this output:
<input type="hidden" id="Country_CountryId" name="Country.CountryId" />
Is there a way to get the id of the above code by using just the model? For eg.
#Html.TestBoxFor(model => model.Country.CountryName, new { data_HiddenId= model.Country.CountryId.GetHtmlRenderOrSomething())
which should give this:
<input type="text" id="Country_CountryName" name="Country.CountryName" data-HiddenId="Country_CountryId" />
My main concern is data-HiddenId. I want it to be equal to the id generated for the hidden input above.
You could try this:
#Html.TestBoxFor(
model => model.Country.CountryName,
new {
data_hiddenid = ExpressionHelper.GetExpressionText((Expression<Func<ModelType, int>>)(x => x.Country.CountryId))
}
)
and to reduce the ugliness you could write a custom HTML helper:
public static class HtmlExtensions
{
public static MvcHtmlString TextBoxWithIdFor<TModel, TProperty, TId>(
this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression,
Expression<Func<TModel, TId>> idExpression
)
{
var id = ExpressionHelper.GetExpressionText(idExpression);
return htmlHelper.TextBoxFor(expression, new { data_hiddenid = id });
}
}
and then simply:
#Html.TestBoxWithIdFor(
model => model.Country.CountryName,
model => model.Country.CountryId
)