ASP.NET MVC 3 - Data Annoation and Max Length/Size for Textbox Rendering - asp.net-mvc-3

I know on the Razor View file, we can do something like this
#Html.TextBox("username", null, new { maxlength = 20, autocomplete = "off" })
However, I am hoping to create a model for the MVC that can be used to create a form with explicitly defined the size and max length of the textboxes. I try [StringLength(n)] on top of the properties of the model, but that seems to only do the validation ratherh set the size of the textbox.
Is there anyway that we can define the length of the text field as a data annotation on top of a property of a model?
So ultimately, we could just create the whole form by using razor to map to a model rather than explicitly pick up the model properties one by one in order to set the textbox size.

Here is a outline of a custom helper that uses StringLengthAttribute.
public class MyModel
{
[StringLength(50)]
public string Name{get; set;}
}
public MvcHtmlString MyTextBoxFor<TModel, TProperty>(this HtmlHelper<TModel> helper,
Expression<Func<TModel, TProperty>> expression)
{
var attributes = new Dictionary<string, Object>();
var memberAccessExpression = (MemberExpression)expression.Body;
var stringLengthAttribs = memberAccessExpression.Member.GetCustomAttributes(
typeof(System.ComponentModel.DataAnnotations.StringLengthAttribute), true);
if (stringLengthAttribs.Length > 0)
{
var length = ((StringLengthAttribute)stringLengthAttribs[0]).MaximumLength;
if (length > 0)
{
attributes.Add("size", length);
attributes.Add("maxlength", length);
}
}
return helper.TextBoxFor(expression, attributes);
}

Does this not work?
public class ViewModel
{
[StringLength(20)]
public string UserName {get;set;}
}
In the View:
#Html.TextBoxFor(x => x.UserName, new {autocomplete = "off"})
or:
#Html.EditorFor(x => x.UserName)

I find that I prefer my views to just Call Html.EditorFor(...). This means that the Editor and Display templates decide the fate of controls in my view, such that my view code gets cleaned up a lot - it just has html and generic requests for editors.
The following link gives a working sample of getting this working in an Editor Template
https://jefferytay.wordpress.com/2011/12/20/asp-net-mvc-string-editor-template-which-handles-the-stringlength-attribute/
I'm using similar in my String.cshtml Editor Template (goes in Shared/EditorTemplates ).
#model object
#using System.ComponentModel.DataAnnotations
#{
ModelMetadata meta = ViewData.ModelMetadata;
Type tModel = meta.ContainerType.GetProperty(meta.PropertyName).PropertyType;
}
#if(typeof(string).IsAssignableFrom(tModel)) {
var htmlOptions = new System.Collections.Generic.Dictionary<string, object>();
var stringLengthAttribute = (StringLengthAttributeAdapter)ViewData.ModelMetadata.GetValidators(this.ViewContext.Controller.ControllerContext).Where(v => v is StringLengthAttributeAdapter).FirstOrDefault();
if (stringLengthAttribute != null && stringLengthAttribute.GetClientValidationRules().First().ValidationParameters["max"] != null)
{
int maxLength = (int)stringLengthAttribute.GetClientValidationRules().First().ValidationParameters["max"];
htmlOptions.Add("maxlength", maxLength);
if (maxLength < 20)
{
htmlOptions.Add("size", maxLength);
}
}
htmlOptions.Add("class", "regular-field");
<text>
#Html.TextBoxFor(m => m, htmlOptions)
</text>
}
else if(typeof(Enum).IsAssignableFrom(tModel)) {
//Show a Drop down for an enum using:
//Enum.GetValues(tModel)
//This is beyond this article
}
//Do other things for other types...
Then my model is annotated such as:
[Display(Name = "Some Field", Description = "Description of Some Field")]
[StringLength(maximumLength: 40, ErrorMessage = "{0} max length {1}.")]
public string someField{ get; set; }
And my View simply calls:
<div class="editor-label">
#Html.LabelWithTooltipFor(model => model.something.someField)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.something.someField)
#Html.ValidationMessageFor(model => model.something.someField)
</div>
You might also notice that my String.cshtml Editor Template also auto-magically handles Enum's, but that is starting to digress from the current topic, so I nixed that code, I'll just say here that the String Editor Template can pull extra weight, and likely google has someting on that https://www.google.com/search?q=string+editor+template+enum
Label With Tooltip For is a custom HTML helper that just drops the description into the label title, for more information on mouse over for every label.
I'd recommend this approach if you want to do this in an Editor Template.

Related

MVC Validate At Least One Checkbox Or a Textbox is Selected

I have a form where either at least one checkbox must be checked OR a textbox is filled in.
I have a ViewModel that populates the CheckboxList and takes the selected values plus the textbox (other) value when required to a SelectedWasteTypes property within the ViewModel. I think my problem is I can't validate against this property as there is no form element on the view that directly relates to it. I've very new to MVC and this one has stumped me.
From the ViewModel
public List<tblWasteTypeWeb> WasteTypeWebs { get; set; }
public string WasteTypeWebOther { get; set; }
public string SelectedWasteTypes { get; set; }
View (segment)
#using (Html.BeginForm("OrderComplete", "Home"))
{
//Lots of other form elements
#for (var i = 0; i < Model.WasteTypeWebs.Count; i++)
{
var wt = Model.WasteTypeWebs[i];
#Html.LabelFor(x => x.WasteTypeWebs[i].WasteTypeWeb, wt.WasteTypeWeb)
#Html.HiddenFor(x => x.WasteTypeWebs[i].WasteTypeWebId)
#Html.HiddenFor(x => x.WasteTypeWebs[i].WasteTypeWeb)
#Html.CheckBoxFor(x => x.WasteTypeWebs[i].WasteTypeWebCb)
}
<br />
<span>
#Html.Label("Other")
#Html.TextBoxFor(x => x.WasteTypeWebOther, new { #class = "form-control input-sm" })
</span>
//More form elements
<input type="submit" value="submit" />
}
Controller Logic (if you can call it that)
[HttpPost]
public ActionResult OrderComplete(OrderViewModel model)
{
var sb = new StringBuilder();
if (model.WasteTypeWebs.Count(x => x.WasteTypeWebCb) != 0)
{
foreach (var cb in model.WasteTypeWebs)
{
if (cb.WasteTypeWebCb)
{
sb.Append(cb.WasteTypeWeb + ", ");
}
}
sb.Remove(sb.ToString().LastIndexOf(",", StringComparison.Ordinal), 1);
}
model.SelectedWasteTypes = sb.ToString();
if (!string.IsNullOrEmpty(model.WasteTypeWebOther))
{
if (!string.IsNullOrEmpty(model.SelectedWasteTypes))
{
model.SelectedWasteTypes = model.SelectedWasteTypes.TrimEnd() + ", " + model.WasteTypeWebOther;
}
else
{
model.SelectedWasteTypes = model.WasteTypeWebOther;
}
}
return View(model);
}
I very much feel I'm up a certain creek... I've thought about using JQuery, but ideally I'd like server side validation to be sure this info is captured (its a legal requirement). However, if this can only be achieved client side, I will live with it.
Any suggestions?
Take a look at the MVC Foolproof Validation Library. It has validation attributes for what you are trying to accomplish: [RequiredIfEmpty] and [RequiredIfNotEmpty]. You can also take a look at my previous SO answer about Conditional Validation.
I would suggest you to implement IValidatableObject in your ViewModel.
Inside Validate( ValidationContext validationContext) method you can check weather your conditions are met. For example:
if(string.IsNullOrWhiteSpace(WasteTypeWebOther))
yield return new ValidationResult("Your validation error here.");

Replacement for TextAreaFor code in Asp.net MVC Razor

Following is my model property
[Required(ErrorMessage = "Please Enter Short Desciption")]
[StringLength(200)]
public string ShortDescription { get; set; }
And following is my corresponding View code
#Html.TextAreaFor(model => model.Product.ShortDescription, new { cols = "50%", rows = "3" })
#Html.ValidationMessageFor(model => model.Product.ShortDescription)
And this is how it shows in the browser, the way i want.
Now, since there is a bug in Microsoft's MVC3 release, I am not able to validate and the form is submitted and produces the annoying error.
Please tell me the work around or any code that can be substituted in place of TextAreaFor. I can't use EditorFor, because with it, i can't use rows and cols parameter. I want to maintain my field look in the browser. Let me know what should be done in this case
In the controller action rendering this view make sure you have instantiated the dependent property (Product in your case) so that it is not null:
Non-working example:
public ActionResult Foo()
{
var model = new MyViewModel();
return View(model);
}
Working example:
public ActionResult Foo()
{
var model = new MyViewModel
{
Product = new ProductViewModel()
};
return View(model);
}
Another possibility (and the one I recommend) is to decorate your view model property with the [DataType] attribute indicating your intent to display it as a multiline text:
[Required(ErrorMessage = "Please Enter Short Desciption")]
[StringLength(200)]
[DataType(DataType.MultilineText)]
public string ShortDescription { get; set; }
and in the view use an EditorFor helper:
#Html.EditorFor(x => x.Product.ShortDescription)
As far as the rows and cols parameters that you expressed concerns in your question about, you could simply use CSS to set the width and height of the textarea. You could for example put this textarea in a div or something with a given classname:
<div class="shortdesc">
#Html.EditorFor(x => x.Product.ShortDescription)
#Html.ValidationMessageFor(x => x.Product.ShortDescription)
</div>
and in your CSS file define its dimensions:
.shortdesc textarea {
width: 150px;
height: 350px;
}

dropdownlist set selected value in MVC3 Razor

Here is my model:
public class NewsCategoriesModel {
public int NewsCategoriesID { get; set; }
public string NewsCategoriesName { get; set; }
}
My controller:
public ActionResult NewsEdit(int ID, dms_New dsn) {
dsn = (from a in dc.dms_News where a.NewsID == ID select a).FirstOrDefault();
var categories = (from b in dc.dms_NewsCategories select b).ToList();
var selectedValue = dsn.NewsCategoriesID;
SelectList ListCategories = new SelectList(categories, "NewsCategoriesID", "NewsCategoriesName",selectedValue);
// ViewBag.NewsCategoriesID = new SelectList(categories as IEnumerable<dms_NewsCategory>, "NewsCategoriesID", "NewsCategoriesName", dsn.NewsCategoriesID);
ViewBag.NewsCategoriesID = ListCategories;
return View(dsn);
}
And then my view:
#Html.DropDownList("NewsCategoriesID", (SelectList)ViewBag.NewsCategoriesID)
When i run, the DropDownList does not select the value I set.. It is always selecting the first option.
You should use view models and forget about ViewBag Think of it as if it didn't exist. You will see how easier things will become. So define a view model:
public class MyViewModel
{
public int SelectedCategoryId { get; set; }
public IEnumerable<SelectListItem> Categories { get; set; }
}
and then populate this view model from the controller:
public ActionResult NewsEdit(int ID, dms_New dsn)
{
var dsn = (from a in dc.dms_News where a.NewsID == ID select a).FirstOrDefault();
var categories = (from b in dc.dms_NewsCategories select b).ToList();
var model = new MyViewModel
{
SelectedCategoryId = dsn.NewsCategoriesID,
Categories = categories.Select(x => new SelectListItem
{
Value = x.NewsCategoriesID.ToString(),
Text = x.NewsCategoriesName
})
};
return View(model);
}
and finally in your view use the strongly typed DropDownListFor helper:
#model MyViewModel
#Html.DropDownListFor(
x => x.SelectedCategoryId,
Model.Categories
)
just in case someone comes with this question, this is how I do it, please forget about the repository object, I'm using the Repository Pattern, you can use your object context to retrieve the entities. And also don't pay attention to my entity names, my entity type Action has nothing to do with an MVC Action.
Controller:
ViewBag.ActionStatusId = new SelectList(repository.GetAll<ActionStatus>(), "ActionStatusId", "Name", myAction.ActionStatusId);
Pay attention that the last variable of the SelectList constructor is the selected value (object selectedValue)
Then this is my view to render it:
<div class="editor-label">
#Html.LabelFor(model => model.ActionStatusId, "ActionStatus")
</div>
<div class="editor-field">
#Html.DropDownList("ActionStatusId")
#Html.ValidationMessageFor(model => model.ActionStatusId)
</div>
I think it is pretty simple, I hope this helps! :)
I drilled down the formation of the drop down list instead of using #Html.DropDownList(). This is useful if you have to set the value of the dropdown list at runtime in razor instead of controller:
<select id="NewsCategoriesID" name="NewsCategoriesID">
#foreach (SelectListItem option in ViewBag.NewsCategoriesID)
{
<option value="#option.Value" #(option.Value == ViewBag.ValueToSet ? "selected='selected'" : "")>#option.Text</option>
}
</select>
Well its very simple in controller you have somthing like this:
-- Controller
ViewBag.Profile_Id = new SelectList(db.Profiles, "Id", "Name", model.Profile_Id);
--View (Option A)
#Html.DropDownList("Profile_Id")
--View (Option B) --> Send a null value to the list
#Html.DropDownList("Profile_Id", null, "-- Choose --", new { #class = "input-large" })
Replace below line with new updated working code:
#Html.DropDownList("NewsCategoriesID", (SelectList)ViewBag.NewsCategoriesID)
Now Implement new updated working code:
#Html.DropDownListFor(model => model.NewsCategoriesID, ViewBag.NewsCategoriesID as List<SelectListItem>, new {name = "NewsCategoriesID", id = "NewsCategoriesID" })
I want to put the correct answer in here, just in case others are having this problem like I was. If you hate the ViewBag, fine don't use it, but the real problem with the code in the question is that the same name is being used for both the model property and the selectlist as was pointed out by #RickAndMSFT
Simply changing the name of the DropDownList control should resolve the issue, like so:
#Html.DropDownList("NewsCategoriesSelection", (SelectList)ViewBag.NewsCategoriesID)
It doesn't really have anything to do with using the ViewBag or not using the ViewBag as you can have a name collision with the control regardless.
I prefer the lambda form of the DropDownList helper - see MVC 3 Layout Page, Razor Template, and DropdownList
If you want to use the SelectList, then I think this bug report might assist - http://aspnet.codeplex.com/workitem/4932
code bellow, get from, goes
Controller:
int DefaultId = 1;
ViewBag.Person = db.XXXX
.ToList()
.Select(x => new SelectListItem {
Value = x.Id.ToString(),
Text = x.Name,
Selected = (x.Id == DefaultId)
});
View:
#Html.DropDownList("Person")
Note:
ViewBag.Person and #Html.DropDownList("Person") name should be as in view model
To have the IT department selected, when the departments are loaded from tblDepartment table, use the following overloaded constructor of SelectList class. Notice that we are passing a value of 1 for selectedValue parameter.
ViewBag.Departments = new SelectList(db.Departments, "Id", "Name", "1");
For anyone that dont want to or dont make sense to use dropdownlistfor, here is how I did it in jQuery with .NET MVC set up.
Front end Javascript -> getting data from model:
var settings = #Html.Raw(Json.Encode(Model.GlobalSetting.NotificationFrequencySettings));
SelectNotificationSettings(settings);
function SelectNotificationSettings(settings) {
$.each(settings, function (i, value) {
$("#" + value.NotificationItemTypeId + " option[value=" + value.NotificationFrequencyTypeId + "]").prop("selected", true);
});
}
In razor html, you going to have few dropdownlist
#Html.DropDownList(NotificationItemTypeEnum.GenerateSubscriptionNotification.ToString,
notificationFrequencyOptions, optionLabel:=DbRes.T("Default", "CommonLabels"),
htmlAttributes:=New With {.class = "form-control notification-item-type", .id = Convert.ToInt32(NotificationItemTypeEnum.GenerateSubscriptionNotification)})
And when page load, you js function is going to set the selected option based on value that's stored in #model.
Cheers.

How do I use editortemplates in MVC3 for complex types?

I have two classes, Vat and Product. Product has a property of IVat. I am trying to use editor templates in MVC to display a dropdown list of all the Vat objects when creating/editing a Product. For the dear life of me I cannot get this working.
I have the following code which displays the dropdown but it does not set the Vat for the Product when the form gets submitted.
Controller:
IList<IVatRate> vatRates = SqlDataRepository.VatRates.Data.GetAllResults();
ViewBag.VatRates = new SelectList(vatRates, "Id", "Description");
Add.cshtml
#Html.EditorFor(model => model.VatRate.Id, "VatSelector", (SelectList)ViewBag.VatRates)
VatSelector.cshtml
#model SelectList
#Html.DropDownList(
String.Empty /* */,
(SelectList)ViewBag.Suppliers,
Model
)
I would be grateful if anyone can shed some light on this or even point me to a good example on the web somewhere...I have been stuck with this for quite a few days now.
I would use strongly typed views and view models as it makes things so much easier rather than ViewBag.
So start with a view model:
public class VatRateViewModel
{
public string SelectedVatRateId { get; set; }
public IEnumerable<IVatRate> Rates { get; set; }
}
then a controller:
public class HomeController: Controller
{
public ActionResult Index()
{
var model = new VatRateViewModel
{
Rates = SqlDataRepository.VatRates.Data.GetAllResults()
};
return View(model);
}
[HttpPost]
public ActionResult Index(VatRateViewModel model)
{
// model.SelectedVatRateId will contain the selected vat rate id
...
}
}
View:
#model VatRateViewModel
#using (Html.BeginForm())
{
#Html.DropDownListFor(
x => x.SelectedVatRateId,
new SelectList(Model.Rates, "Id", "Description")
)
<input type="submit" value="OK" />
}
And if you wanted to use editor template for the VatRateViewModel you could define one in ~/Views/Shared/EditorTemplates/VatRateViewModel.cshtml:
#model VatRateViewModel
#Html.DropDownListFor(
x => x.SelectedVatRateId,
new SelectList(Model.Rates, "Id", "Description")
)
and then whenever somewhere you have a property of type VatRateViewModel you could simply:
#Html.EditorFor(x => x.SomePropertyOfTypeVatRateViewModel)
which would render the corresponding editor template.

How to handle Dropdownlist in mvc asp .net

this is my Model1 class
namespace chetan.Models
{
public class Model1
{
public string selectedItem { get; set; }
public IEnumerable<SelectListItem> items { get; set; }
}
}
this is my controller class
public class HomeController : Controller
{
private rikuEntities rk = new rikuEntities();
public ActionResult Index()
{
var model = new Model1
{
items = new[]
{
new SelectListItem { Value = "Theory", Text = "Theory" },
new SelectListItem { Value = "Appliance", Text = "Appliance" },
new SelectListItem { Value = "Lab", Text = "Lab" }
}
}; return View(model);
}
public ActionResult viewToController(Model1 m)
{
string getSelectedName = m.selectedItem;
return Content(getSelectedName);
}
}
this is my view...
#using (Html.BeginForm("viewToController", "Home"))
{
#Html.ValidationSummary(true)
<fieldset>
<legend>emp</legend>
<div class="editor-field">
#Html.DropDownListFor(x => x.selectedItem,
new SelectList(Model.items, "Value", "Text"))
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
i want to add a drop downlist and i want to use selected value in viewToController action of homeController. and there is also one error in View page is "an expression tree may not contain dynamic operation" in (x=>x.selectedItem). Please solve my problem .
I don't understnad what you exactly need. You want to dynamicly add items to the drop down from the database?
I'm big fan of jQuery. You can do everything what you want with HTML using jQuery. So if you are looking how to automaticly add items to the drop down, take look at this: How do I add options to a DropDownList using jQuery?

Resources