I need your help in creating a textbox readonly property true or false based on a condition.
I tried however was unsuccessful.
Below is my sample code:
string property= "";
if(x=true)
{
property="true"
}
#Html.TextBoxFor(model => model.Name, new { #readonly = property})
My question is: Even though the condition is false I am unable to write or edit the textbox?
This is because the readonly attribute in HTML is designed so that it's mere presence indicates a readonly textbox.
I believe that the values true|false are completely ignored by the attribute and infact the recomended value is readonly="readonly".
To re-enable the textbox you'll need to get rid of the readonly attribute altogether.
Given that the htmlAttributes property of TextBoxFor is an IDictionary, you could simply build the object based on your requirements.
IDictionary customHTMLAttributes = new Dictionary<string, object>();
if(x == true)
// Notice here that i'm using == not =.
// This is because I'm testing the value of x, not setting the value of x.
// You could also simplfy this with if(x).
{
customHTMLAttributes.Add("readonly","readonly");
}
#Html.TextBoxFor(model => model.Name, customHTMLAttributes)
A shorthand way to add the custom attrbute could be:
var customHTMLAttributes = (x)? new Dictionary<string,object>{{"readonly","readonly"}}
: null;
or simply:
#Html.TextBoxFor(model => model.Name, (x)? new {"readonly","readonly"} : null);
I achieved it using some extension methods
public static MvcHtmlString IsDisabled(this MvcHtmlString htmlString, bool disabled)
{
string rawstring = htmlString.ToString();
if (disabled)
{
rawstring = rawstring.Insert(rawstring.Length - 2, "disabled=\"disabled\"");
}
return new MvcHtmlString(rawstring);
}
public static MvcHtmlString IsReadonly(this MvcHtmlString htmlString, bool #readonly)
{
string rawstring = htmlString.ToString();
if (#readonly)
{
rawstring = rawstring.Insert(rawstring.Length - 2, "readonly=\"readonly\"");
}
return new MvcHtmlString(rawstring);
}
and then....
#Html.TextBoxFor(model => model.Name, new { #class= "someclass"}).IsReadonly(x)
You probably need to refactor your code to be something along the lines of
if(x)
{
#Html.TextBoxFor(model => model.Name, new { #readonly = "readonly"})
}
else
{
#Html.TextBoxFor(model => model.Name)
}
Related
I'm unable to display custom text for enum. When I try to display I get Slot1, Slot2, etc.
What do I do to get the custom text?
public enum TimeSlots
{
[Display(Name =#"11:00AM - 12:00PM")]
Slot1,
[Display(Name = #"12:00PM - 10:00PM")]
Slot2,
[Display(Name =#"01:00PM - 02:00PM")]
Slot3,
[Display(Name = "02:00PM - 03:00PM")]
Slot4,
[Display(Name = "03:00PM - 04:00PM")]
Slot5,
[Display(Name = "04:00PM - 05:00PM")]
Slot6
}
Following is the code for the DropDownList:
<label asp-for="TimeSlot"></label>
#Html.DropDownListFor(m => m.TimeSlot,
new SelectList(Enum.GetValues(typeof(TimeSlots))),"Select TimeSlot",new { #class = "form-control" })
Try the following:
#Html.DropDownList("enumlist1", Enum.GetValues(typeof(TimeSlots))
.Cast<TimeSlots>()
.Select(e => new SelectListItem() { Value = e.ToString(), Text = e.GetDisplayName() }))
The code above uses the following extension method:
public static class EnumExtensions
{
public static string GetDisplayName(this Enum value)
{
return value.GetType()
.GetMember(value.ToString())
.First()
.GetCustomAttribute<DisplayAttribute>()
?.GetName();
}
}
The created drop-down list looks like below:
I am trying to extend the SelectListItem class to add another property called CardColor. However when I try to access the property in my controller I get
NullReferenceException: Object reference not set to an instance of an object....
return View("StringView", c.IssueSelected.CardColor);
Controller:
[HttpPost]
public async Task<ActionResult> CardCreate(UpdateCardFormOptions c)
{
return View("StringView", c.IssueSelected.CardColor);
}
View
#using (#Html.BeginForm())
{
<p><label>Issue Category*:</label> #Html.DropDownListFor(c => #Model.IssueSelected, Model.IssueList, new { #class = "form-control", #style = "width: 350px" })</p>
}
Model:
public IssueSelectListItem IssueSelected { get; set; }
public List<IssueSelectListItem> IssueList = new List<IssueSelectListItem>() {
new IssueSelectListItem() {Text="xxx", Value="yyy",CardColor="pink"},
};
public class IssueSelectListItem : SelectListItem
{
public string CardColor { get; set; }
}
This post gave me a clue, turns out I needed to set the value CardColor in the View, I couldnt just set the object equivalent. My View needed to set CardColor based on my dropdown choice like this:
<p><label>Issue Category*:</label> #Html.DropDownListFor(c => #Model.IssueSelected.CardColor, Model.IssueList, new { #class = "form-control", #style = "width: 350px" })</p>
Not going to accept my own answer, still hoping someone has a better one, this just solved my immediate problem
Conceptually I would like the following code to work:
#Html.TextBoxFor(x => x.Something, null, new {
#class = "custom",
data_min = x.min,
data_max = x.max,
data_step = x.step
})
Of course it doesn't it seems I can't access the properties min, max etc from the extended attributes section.
How can I implement this?
Thanks.
Just use the Model:
#Html.TextBoxFor(
x => x.Something,
new {
#class = "custom",
data_min = Model.min,
data_max = Model.max,
data_step = Model.step
}
)
Add this property to your model
public IDictionary<string, object> Attributes { get; set; }
Then
#Html.TextBoxFor(model => model.SomeValue, Model.Attributes)
I need to apply a custom validator to billing address fields. The view displays several address fields, including a Country drop-down list (with U.S. and Canada options), and a BillingPostalCode textbox. Originally I applied a regular expression to the message contract that allowed either U.S. or Canada zip codes, like so:
[MessageBodyMember]
[Display(Name = "Billing Postal Code")]
[Required]
[StringLength(10, MinimumLength = 5)]
[RegularExpression("(^\\d{5}(-\\d{4})?$)|(^[ABCEGHJKLMNPRSTVXY]{1}\\d{1}[A-Z]{1} *\\d{1}[A-Z]{1}\\d{1}$)", ErrorMessage = "Zip code is invalid.")] // US or Canada
public string BillingPostalCode
{
get { return _billingPostalCode; }
set { _billingPostalCode = value; }
}
The above will allow either U.S. or Canada zip codes. But, the business owner wants the form to allow a U.S. or Canadian zip code only when United States or Canada is respectively selected in the BillingCountry drop-down list. In his test case, he selected Canada and entered a U.S. zip code. That scenario should be disallowed.
My initial stab at doing this was to put this in the view, though I'm not happy with creating 2 textbox fields. I should only need 1 field.
<div style="float: left; width: 35%;">
Zip Code<br />
<span id="spanBillingZipUS">
#Html.TextBoxFor(m => m.BillingPostalCode, new { #class = "customer_input", #id = "BillingPostalCode" })
</span>
<span id="spanBillingZipCanada">
#Html.TextBoxFor(m => m.BillingPostalCode, new { #class = "customer_input", #id = "BillingPostalCodeCanada" })
</span>
#Html.ValidationMessageFor(m => m.BillingPostalCode)
<br />
</div>
My thought process being that I'll use jQuery to show or hide the appropriate span when the Country drop-down list is toggled. That piece is easy.
But I'm stuck with the problem that both text boxes have that single validator applied to them, which maps to the MessageBodyMember pasted above. I know how to write the validation code in jQuery, but would prefer to also have the validation applied to the server side as well.
I'm fairly new to MVC, having come from web forms. The "old school" web forms custom validation was simple to implement. Examples I've found online for custom validation in MVC are pretty complex. At first, this seemed to be a very basic request. The code needs to evaluate one variable (the selected Country) and apply the appropriate regular expression for that country to the BillingPostalCode field.
How do I meet this requirement in a straightforward fashion with MVC3? Thanks.
Well I implemented what this guy made and it works as a chram with Data Annotations. You do have to work a little in order to change the check for a dropdown value, but this is the more elegant way I found to implement validation with Data Annotations and Unobtrusive.
Here an example:
Model
...
[RequiredIf("IsUKResident", true, ErrorMessage = "You must specify the City if UK resident")]
public string City { get; set; }
...
Custom Attribute
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
namespace Mvc3ConditionalValidation.Validation
{
public class RequiredIfAttribute : ValidationAttribute, IClientValidatable
{
private RequiredAttribute _innerAttribute = new RequiredAttribute();
public string DependentProperty { get; set; }
public object TargetValue { get; set; }
public RequiredIfAttribute(string dependentProperty, object targetValue)
{
this.DependentProperty = dependentProperty;
this.TargetValue = targetValue;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
// get a reference to the property this validation depends upon
var containerType = validationContext.ObjectInstance.GetType();
var field = containerType.GetProperty(this.DependentProperty);
if (field != null)
{
// get the value of the dependent property
var dependentvalue = field.GetValue(validationContext.ObjectInstance, null);
// compare the value against the target value
if ((dependentvalue == null && this.TargetValue == null) ||
(dependentvalue != null && dependentvalue.Equals(this.TargetValue)))
{
// match => means we should try validating this field
if (!_innerAttribute.IsValid(value))
// validation failed - return an error
return new ValidationResult(this.ErrorMessage, new[] { validationContext.MemberName });
}
}
return ValidationResult.Success;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule()
{
ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
ValidationType = "requiredif",
};
string depProp = BuildDependentPropertyId(metadata, context as ViewContext);
// find the value on the control we depend on;
// if it's a bool, format it javascript style
// (the default is True or False!)
string targetValue = (this.TargetValue ?? "").ToString();
if (this.TargetValue.GetType() == typeof(bool))
targetValue = targetValue.ToLower();
rule.ValidationParameters.Add("dependentproperty", depProp);
rule.ValidationParameters.Add("targetvalue", targetValue);
yield return rule;
}
private string BuildDependentPropertyId(ModelMetadata metadata, ViewContext viewContext)
{
// build the ID of the property
string depProp = viewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(this.DependentProperty);
// unfortunately this will have the name of the current field appended to the beginning,
// because the TemplateInfo's context has had this fieldname appended to it. Instead, we
// want to get the context as though it was one level higher (i.e. outside the current property,
// which is the containing object (our Person), and hence the same level as the dependent property.
var thisField = metadata.PropertyName + "_";
if (depProp.StartsWith(thisField))
// strip it off again
depProp = depProp.Substring(thisField.Length);
return depProp;
}
}
}
Client Side
...
<script src="#Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>
<script>
$.validator.addMethod('requiredif',
function (value, element, parameters) {
var id = '#' + parameters['dependentproperty'];
// get the target value (as a string,
// as that's what actual value will be)
var targetvalue = parameters['targetvalue'];
targetvalue =
(targetvalue == null ? '' : targetvalue).toString();
// get the actual value of the target control
// note - this probably needs to cater for more
// control types, e.g. radios
var control = $(id);
var controltype = control.attr('type');
var actualvalue =
controltype === 'checkbox' ?
control.attr('checked').toString() :
control.val();
// if the condition is true, reuse the existing
// required field validator functionality
if (targetvalue === actualvalue)
return $.validator.methods.required.call(
this, value, element, parameters);
return true;
}
);
$.validator.unobtrusive.adapters.add(
'requiredif',
['dependentproperty', 'targetvalue'],
function (options) {
options.rules['requiredif'] = {
dependentproperty: options.params['dependentproperty'],
targetvalue: options.params['targetvalue']
};
options.messages['requiredif'] = options.message;
});
</script>
...
<div class="editor-label">
#Html.LabelFor(model => model.City)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.City)
#Html.ValidationMessageFor(model => model.City)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.IsUKResident)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.IsUKResident)
#Html.ValidationMessageFor(model => model.IsUKResident)
</div>
...
This doesn't sound like it will be something easy to accomplish using ModelBinding and validation attributes.
A straight forward way to do it could be that you leave the validation attribute as is and simply validate if its a USA or Canadian postcode. Then when it gets to the server, do some manual validation.
E.g.
[Post]
public ActionResult SaveInfo(MyViewModel viewModel)
{
var isValid = true;
if (ModelState.IsValid)
{
if (!IsValidPostCode(viewModel))
{
isValid = false;
ModelState.AddModelError("BillingPostalCode", "The billing postcode appears to be invalid.");
}
if (isValid)
{
return RedirectToAction("success");
}
}
return View(viewModel);
}
private static IDictionary<string, string> countryPostCodeRegex = new Dictionary<string, string>
{
{ "USA", "USAPostCodeRegex" },
{ "Canada", "CanadianPostCodeRegex" },
}
private bool IsValidPostCode(MyViewModel viewModel)
{
var regexString = countryPostCodeRegex[viewModel.SelectedCountry];
var regexMatch = Regex.Match(viewModel.BillingPostalCode, regexString, RegexOptions.IgnoreCase);
return regexMatch.Success;
}
I used MVC Foolproof Validation in one of my projects. You would do as you were saying with hiding and showing fields, but the validation would follow through the same logic. Have 2 fields for postal code. One for Canada, the other for USA. They each have their own regex validation to ensure the proper format.
Pseudo Code
[RequiredIf(DependentName = "Country", DependentValue="USA")]
public string PostalCodeUSA { get; set; }
[RequiredIf(DependentName = "Country", DependentValue="Canada")]
public string PostalCodeCanada { get; set; }
Rookie question.
I have a parameter being passed to a create view. I need to set a field name with a default value.
#Html.EditorFor(model => model.Id)
I need to set this input field with name Id with a default value that is being passed to the view via an actionlink.
So, how can this input field --#Html.EditorFor(model => model.Id) -- get set with a default value.
Would the following work?? Where the number 5 is a parameter I pass into the text field to set default value.
#Html.EditorFor(c => c.PropertyName, new { text = "5"; })
Here's what I've found:
#Html.TextBoxFor(c => c.Propertyname, new { #Value = "5" })
works with a capital V, not a lower case v (the assumption being value is a keyword used in setters typically) Lower vs upper value
#Html.EditorFor(c => c.Propertyname, new { #Value = "5" })
does not work
Your code ends up looking like this though
<input Value="5" id="Propertyname" name="Propertyname" type="text" value="" />
Value vs. value. Not sure I'd be too fond of that.
Why not just check in the controller action if the proprety has a value or not and if it doesn't just set it there in your view model to your defaulted value and let it bind so as to avoid all this monkey work in the view?
The clean way to do so is to pass a new instance of the created entity through the controller:
//GET
public ActionResult CreateNewMyEntity(string default_value)
{
MyEntity newMyEntity = new MyEntity();
newMyEntity._propertyValue = default_value;
return View(newMyEntity);
}
If you want to pass the default value through ActionLink
#Html.ActionLink("Create New", "CreateNewMyEntity", new { default_value = "5" })
Its not right to set default value in View. The View should perform display work, not more. This action breaks ideology of MVC pattern. So the right place to set defaults - create method of controller class.
Better option is to do this in your view model like
public class MyVM
{
int _propertyValue = 5;//set Default Value here
public int PropertyName{
get
{
return _propertyValue;
}
set
{
_propertyValue = value;
}
}
}
Then in your view
#Html.EditorFor(c => c.PropertyName)
will work the way u want it (if no value default value will be there)
I just did this (Shadi's first answer) and it works a treat:
public ActionResult Create()
{
Article article = new Article();
article.Active = true;
article.DatePublished = DateTime.Now;
ViewData.Model = article;
return View();
}
I could put the default values in my model like a propper MVC addict: (I'm using Entity Framework)
public partial class Article
{
public Article()
{
Active = true;
DatePublished = Datetime.Now;
}
}
public ActionResult Create()
{
Article article = new Article();
ViewData.Model = article;
return View();
}
Can anyone see any downsides to this?
Shouldn't the #Html.EditorFor() make use of the Attributes you put in your model?
[DefaultValue(false)]
public bool TestAccount { get; set; }
Shove it in the ViewBag:
Controller:
ViewBag.ProductId = 1;
View:
#Html.TextBoxFor(c => c.Propertyname, new {#Value = ViewBag.ProductId})
This worked for me
In Controlle
ViewBag.AAA = default_Value ;
In View
#Html.EditorFor(model => model.AAA, new { htmlAttributes = new { #Value = ViewBag.AAA } }
For me I need to set current date and time as default value this solved my issue in View add this code :
<div class="form-group">
#Html.LabelFor(model => model.order_date, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.order_date, new { htmlAttributes = new { #class = "form-control",#Value= DateTime.Now } })
#Html.ValidationMessageFor(model => model.order_date, "", new { #class = "text-danger" })
</div>
</div>
This worked for me:
In the controller
*ViewBag.DefaultValue= "Default Value";*
In the View
*#Html.EditorFor(model => model.PropertyName, new { htmlAttributes = new { #class = "form-control", #placeholder = "Enter a Value", #Value = ViewBag.DefaultValue} })*
In the constructor method of your model class set the default value whatever you want.
Then in your first action create an instance of the model and pass it to your view.
public ActionResult VolunteersAdd()
{
VolunteerModel model = new VolunteerModel(); //to set the default values
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult VolunteersAdd(VolunteerModel model)
{
return View(model);
}
This is my working code:
#Html.EditorFor(model => model.PropertyName, new { htmlAttributes = new { #class = "form-control", #Value = "123" } })
my difference with other answers is using Value inside the htmlAttributes array
Instead of using controller or html helper.You can also use Jquery to set default value to model attribute.
$('#PropertyName').val(15);