I am trying to implement a jquery datepicker in my MVC 3 application. It works fine in edit mode, but when trying to use it in the create view, I get the null dictionary exception which tells me it cannot take a null value and needs a value of DateTime. Of course it's going to be null, your trying to create a new field. How can I get this to work.
I would use EditorTemplate instead of custom helper
Create new partial view Date.ascx and place it in \Views\Shared\EditorTemplates\
<%# Control Language="C#" %>
<%: Html.TextBox("", Model == null ? "" : ((DateTime)Model).ToString("yyyy-MM-dd"), new { #class = "datepicker", #readonly = "readonly" })%>
That works fine for all properties of type DateTime
A lot easier using custom helper, im using helper below in one of my application.
1.Create Helper class
namespace System.Web.Mvc.Html
{
public static class DatePickerHelper
{
public static string DatePicker(this HtmlHelper htmlHelper, string id, string name, string value)
{
StringBuilder sBuilder = new StringBuilder();
sBuilder.AppendLine("<script language=\"javascript\" type=\"text/javascript\">");
sBuilder.AppendLine("$(function () {");
sBuilder.AppendLine("$(\"#" + id + "\").datepicker({");
sBuilder.AppendLine("showOn: \"button\",");
sBuilder.AppendLine("buttonImage: \"/Content/images/icon-calendar.gif\",");
sBuilder.AppendLine("dateFormat: 'dd/mm/yy',");
sBuilder.AppendLine("buttonImageOnly: true");
sBuilder.AppendLine(" });");
sBuilder.AppendLine("});");
sBuilder.AppendLine("</script>");
sBuilder.AppendLine("<input type=\"text\" value=\"" + value + "\" id=\"" + id + "\" name=\""+name+"\" class=\"SmallTextBox\" />");
return sBuilder.ToString();
}
}
}
use it like this on your view (For creating)
in my case i have Controller call Employee, make sure you use this pattern if you are using data model { "YouControllerName.PropertyName" and "YouControllerName_PropertyName"
<%= Html.DatePicker("Employee_StartDate","Employee.StartDate","") %>
For editing
<%=Html.DatePicker("Employee_StartDate","Employee.StartDate",Model.Employee.StartDate.ToShortDateString()) %>
Related
I am new to MVC 3 and have this question to start with,
I have a class defined as
Class abc
{ public string Id { get; set; }
public string str1 { get; set; }
public string Action { get; set; }
public string Name { get; set; }
public string Title {get; set;}
}
on my MVC2 aspx viewpage , I was using this class abc as model and had this code
<%
Model.ForEach(a =>
{ %>
<%= Html.ActionLink(a.Title ,
a.Action , // <-- ActionMethod
a.Name , // <-- Controller Name.
new { key = a.Id }, // <-- Route arguments.
new { title = a.str1 })%>
<br /><br />
<% }); %>
can you please help me convert this piece of code to MVC razor view ?,
#model abc
<%
Model.ForEach(a =>
{ %>
<%= Html.ActionLink(a.Title ,
a.Action , // <-- ActionMethod
a.Name , // <-- Controller Name.
new { key = a.Id }, // <-- Route arguments.
new { title = a.str1 })%>
<br /><br />
<% }); %>
when I try to use #foreach ( var abc in Model) , I get error message , need to implement Ienumerable ? How can I implement using #for Please help or give me pointers.Thanks
The model shows only one object, not a list of objects.
Therefore you should not use ForEach but access the properties directly without a loop as Model.Name etc.
If you want a list of objects, then you need to update your controller to return a list of those by using a generic list, for example.
var abcCollection = new List<abc>();
That should point you in the right direction.
user1005310,
a bit of understanding of the Razor syntax will help here. there are plenty of examples out there via Mr google. However, if you have a LOAD of code to convert, then you have a great little 'tool' out there to help (now OSS, originally developed by Telerik). Take a look at:
https://github.com/telerik/razor-converter
this is basically a convertor that takes an entire set of aspx views and converts them to Razor. I've tried it on a few test projects now and it works to 99% of my satisfaction, the other 1% is being addressed (or i can live with the minor tweaking).
I'd recommend you using a display template. This way you don't need to write any loops. So:
#model IEnumerable<abc>
#Html.DisplayForModel()
and then you define a display template which will automatically be rendered for each element of the model collection (~/Views/Shared/DisplayTemplates/abc.cshtml):
#model abc
#Html.ActionLink(
Model.Title,
Model.Action,
Model.Name,
new { key = Model.Id },
new { title = Model.str1 }
)
<br /><br />
Notice that templates work by convention. They must be placed in either the ~/Views/Shared/DisplayTemplates folder or the ~/Views/SomeController/DisplayTemplates folder depending on whether you want to reuse them between views from multiple controllers or a single controller. ASP.NET MVC first looks in the specific folder for a template and then in the Shared. The name of the file is also important. In this case your model consists of an IEnumerable<abc> where abc is the type of the elements in this collection therefore the display template must be called abc.html.
Same rules apply for editor templates. Just replace display by editor. Editor templates, as their name suggests, are suitable for putting input fields for editing a view model.
If I am passing HtmlAttributes into a template, like this:
#Html.DisplayFor(m => m.FirstName, new { htmlAttributes = new { #class = "orangetxt strongtxt" } })
In my template, how would I inject these into my HTML:
<span #ViewData["htmlAttributes"]>#Model</span>
This almost works, but it does some pretty weird stuff, so I'm assuming this isn't the way to go.
I realize I can accomplish this with an HtmlHelper extension method to render the full HTML element (span, in this case) and pass in the attributes that way, but is there a way to just render attributes straight into an HTML element, like the above example?
The below extension method will allow me to convert HtmlAttributes to a string:
public static MvcHtmlString RenderHtmlAttributes<TModel>(
this HtmlHelper<TModel> htmlHelper, object htmlAttributes)
{
var attrbituesDictionary = new RouteValueDictionary(htmlAttributes);
return MvcHtmlString.Create(String.Join(" ",
attrbituesDictionary.Select(
item => String.Format("{0}=\"{1}\"", item.Key,
htmlHelper.Encode(item.Value)))));
}
Then, to render them within the tag, I can just do this:
<span #Html.RenderHtmlAttributes(ViewData["htmlAttributes"])>#Model</span>
Jerad Rose's answer is good, but I ran into couple of issues with it:
It does not not convert underscores to dashes in attribute names
It does not handle no-value attributes gracefully
To address first issue, use HtmlHelper.AnonymousObjectToHtmlAttributes.
Below is my modification of Jerad's method:
public static MvcHtmlString RenderHtmlAttributes(this HtmlHelper helper, object htmlAttributes)
{
if (htmlAttributes == null) return new MvcHtmlString(String.Empty);
var attrbituesDictionary = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
return new MvcHtmlString(String.Join(" ", attrbituesDictionary.Select(item => string.IsNullOrEmpty((string)item.Value) ? String.Format("{0}", item.Key) : String.Format("{0}=\"{1}\"", item.Key, helper.Encode(item.Value)))));
}
Try this instead,
#Html.DisplayFor(m => m.FirstName,
new { htmlAttributes = "class = orangetxt strongtxt"})
This will render a string, whereas your version did do weird stuff, rendered { } as part of the output.
DisplayFor() is used to render the template that matches the property type.
Display templates are .cshtml files inside /DisplayTemplates folder which in turn is inside a view folder (i.e. any folder from Home, Shared or even a specific controller).
An example.
If you've a String.cshtml template like this inside /Views/Shared:
#model String
#if (string.IsNullOrEmpty(Model)) {
<span>(no string)</span>
}
else {
<span>#Model</span>
}
Every time you call DisplayFor() for a string property:
DisplayFor(model => model.MyStringProperty);
It renders the template accordingly to the string's value. You can be more specific and put /DisplayTemplates inside a specific View folder and them only calls from those views are affected by the template.
In your case you can be even more specific and call DisplayFor() with a particular template.
Suppose you've a template for a particular property, called MyPropertyTemplate.cshtml. You would call DisplayFor() like this:
DisplayFor(model => model.MyProperty, "MyPropertyTemplate");
And them, inside that template you can have whatever HTML attributes you want.
#model MyProperty
<span class="orangetxt strongtxt">#MyProperty.ToString()</span>
PS: When it doesn't find a template I guess it only calls model.Property.ToString() without additional html.
FYI: EditorFor(), for example, works in a similar way but it uses /EditorTemplates folder.
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.
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.
The problem is: when I put 2 controls of the same type on a page I need to specify different prefixes for binding. In this case the validation rules generated right after the form are incorrect. So how to get client validation work for the case?:
the page contains:
<%
Html.RenderPartial(ViewLocations.Shared.PhoneEditPartial, new PhoneViewModel { Phone = person.PhonePhone, Prefix = "PhonePhone" });
Html.RenderPartial(ViewLocations.Shared.PhoneEditPartial, new PhoneViewModel { Phone = person.FaxPhone, Prefix = "FaxPhone" });
%>
the control ViewUserControl<PhoneViewModel>:
<%= Html.TextBox(Model.GetPrefixed("CountryCode"), Model.Phone.CountryCode) %>
<%= Html.ValidationMessage("Phone.CountryCode", new { id = Model.GetPrefixed("CountryCode"), name = Model.GetPrefixed("CountryCode") })%>
where Model.GetPrefixed("CountryCode") just returns "FaxPhone.CountryCode" or "PhonePhone.CountryCode" depending on prefix
And here is the validation rules generated after the form. They are duplicated for the field name "Phone.CountryCode". While the desired result is 2 rules (required, number) for each of the FieldNames "FaxPhone.CountryCode", "PhonePhone.CountryCode"
alt text http://www.freeimagehosting.net/uploads/37fbe720bf.png
The question is somewhat duplicate of Asp.Net MVC2 Clientside Validation and duplicate ID's problem
but the advise to manually generate ids doesn't helps.
Correct way to set the same prefixes both for textbox and validation:
<% using (Html.BeginHtmlFieldPrefixScope(Model.Prefix)) { %>
<%= Html.TextBoxFor(m => m.Address.PostCode) %>
<%= Html.ValidationMessageFor(m => m.Address.PostCode) %>
<% } %>
where
public static class HtmlPrefixScopeExtensions
{
public static IDisposable BeginHtmlFieldPrefixScope(this HtmlHelper html, string htmlFieldPrefix)
{
return new HtmlFieldPrefixScope(html.ViewData.TemplateInfo, htmlFieldPrefix);
}
private class HtmlFieldPrefixScope : IDisposable
{
private readonly TemplateInfo templateInfo;
private readonly string previousHtmlFieldPrefix;
public HtmlFieldPrefixScope(TemplateInfo templateInfo, string htmlFieldPrefix)
{
this.templateInfo = templateInfo;
previousHtmlFieldPrefix = templateInfo.HtmlFieldPrefix;
templateInfo.HtmlFieldPrefix = htmlFieldPrefix;
}
public void Dispose()
{
templateInfo.HtmlFieldPrefix = previousHtmlFieldPrefix;
}
}
}
(by chance found the solution in the code on Steve Sanderson's blog http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/)
Also looks like Html.EditorFor approach should work as well as suggested here: ASP.NET MVC 2 - ViewModel Prefix