So I have this form where the user can enter in a start time and end time (for a booking). The values in the database are DateTimes, but the Date part is completely ignored. Currently, when I enter in eg. "5:00 pm" (through a jQuery time picker), it says this is not a valid date. If I type "30/1/2013 5:00 pm" it accepts this as valid... How do I change this behaviour to only validate as a time? (or if that's not ideal, how can I completely turn validation off for that field - I'll manually handle validation in the controller)
I saw this: http://connect.microsoft.com/VisualStudio/feedback/details/705643/a-data-val-date-attribute-is-generated-for-time-fields-in-asp-net-mvc-4# but Microsoft claims it has been fixed; not sure if this is the same problem. I don't want to change the database definition to TimeSpan because I believe it will muck up the existing data (it will won't it?).
Controller:
[HttpPost]
public ActionResult Create(BookingDetailsDate bookingdetailsdates) //, FormCollection collection)
{
if (ModelState.IsValid)
{
try
{
//bookingdetailsdates.StartTime = DateTime.Parse(collection["StartTime"]); // TODO: do this better
//bookingdetailsdates.EndTime = DateTime.Parse(collection["EndTime"]);
bookingdetailsdatesRepository.Add(bookingdetailsdates);
bookingdetailsdatesRepository.Save();
return RedirectToAction("Index", new { id = bookingdetailsdates.BookingDetailsID });
}
catch { }
}
return View(bookingdetailsdates);
}
View:
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend></legend>
...
#Html.EditorFor(model => model.StartTime)
#Html.EditorFor(model => model.EndTime)
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
Editor Template:
<div class="editor-label">
#Html.LabelFor(m => m)
</div>
<div class="editor-field">
#if (Model.HasValue==true)
{
#Html.TextBox("", Model.Value.ToShortTimeString(), new { #class = "TimePicker" })
}
else
{
#Html.TextBoxFor(m => m, new { #class = "TimePicker" })
}
#Html.ValidationMessageFor(m => m)
</div>
I think "DateTime" this data type is not a good choice, if you just want to save time that users save and then compare them in somewhere I suggest to use "nchar" type. Why? Let me explain, datetime is a very complicated type, it has many kinds of forms. Like "2013-1-29 5:00 pm", "2013/1/29 5:00pm". If your purpose is to compare and check if out of date, you can use this way. "201301291700", it's a fixed length which is 12. The benefit of this is 1. you can easy to save it anywhere by type string. 2. you can compare easily by convert to int type or maybe long type.(number is much easier to compare, isn't it?)
Answer is based on my experience, hope it helps you!
So the trouble was in jQuery's validation, even though the metadata for my fields specified it would be a time only (and that wasn't even strictly necessary), jQuery's validate requires the box to contain a full datetime string...
Commenting out these lines fixes the problem:
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
Related
<div class="input-group" style="margin-bottom:30px;">
<span class="input-group-addon">
Brand
</span>
#Html.DropDownListFor(x => x.BrandMod.Id, c,"- Select Brand - ", new { #class = "form-control" ,id="brandid" })
</div>
"This is My Code for Drop Down and I want to select the selected item. I am trying this"
$("#brandid").change(function (rupp) {
var a = $(this).select("option:selected").val();
});
"I am Unable to Load the Selected item."
This doesn't do what you're thinking:
$(this).select("option:selected").val()
In fact, it's probably either silently failing entirely or producing an error. But no matter. What you're trying to do is get the value from this (which is a <select> in your case). That can be done simply with:
$(this).val()
You don't need Specification for getting value in jquery. You have already change event for this
$("#brandid").change(function (rupp) {
var a = $(this).val();
});
or
var a = $("#brandid").val();
I have a basic form allowing users to input details which then gets posted and saved to a database - this works as expected without any issues:
#model R32.Register.Models.RegisterCar
#{
ViewBag.Title = "Edit Your R32";
}
<h2>Edit R32</h2>
<div>
#using (Html.BeginForm("UpdateCar", "Garage", FormMethod.Post))
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Enter details</legend>
<ol>
<li>
#Html.LabelFor(m => m.NumberPlate)
#Html.EditorFor(m => m.NumberPlate, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.NumberPlate)
</li>
<li>
#Html.LabelFor(m => m.Edition)
#Html.EnumDropDownListFor(m => m.Edition, "Select an edition:", new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Edition)
</li>
<li>
#Html.LabelFor(m => m.Colour)
#Html.EnumDropDownListFor(m => m.Colour, "Select a colour:", new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Colour)
</li>
</ol>
<input type="submit" value="Save Changes" />
</fieldset>
}
</div>
Model snippet:
[Required]
[Display(Name="Edition")]
public MkEnum? Edition { get; set; }
Enum:
public enum MkEnum
{
[Display(Name="Mk4")]
Mk4 = 1,
[Display(Name="Mk5")]
Mk5 = 2
}
The control renders as expected, with the Edition dropdownlist having three values: "Select an edition", "Mk4", and "Mk5".
The user is able to select an edition, control is validated, then posted to the controller.
The Post is successful, and all selected values are sent to the controller - the app then persists the data in a database, and so on, without any problems.
The issue is when I pass this model back into the same View to allow the user to edit the saved data, the saved values for the enums are NOT being set as the selected value in the dropdownlist.
I can confirm that any saved string values, such as NumberPlate in this example, are being passed back into the view and loaded into the UI.
Putting a breakpoint on the viewmodel as it renders I can confirm that my #model contains the saved values for enum properties - Edition for example - but the end result is that the "Select an edition:" dropdown list is rendered containing the expected dropdown values, but it's value is the default "Select an edition:" instead of the actual value passed in via. m.Edition.
I have been able to get this working using DropDownListFor - but am having difficulties in understanding why this is not working using EnumDropDownListFor, as this clearly seems like a more elegant solution.
Does anyone have any help/advice for this?
I just ran into this problem myself. This happens because fields of type enum are being passed back to the browser serialized as their enum names, but #Html.EnumDropDownListFor generates its option values as integers. The browser can't match up the two so the dropdown stays at its default selection.
There are 3 ways to get around this.
Get the view model's enum field to serialize properly as an int.
Write a dropdown generator that uses enum names as option values.
Use javascript to manually select the option (includes razor syntax here)
$("#YourDropdownID option").each(function () {
if ($(this).html() == '#(Html.DisplayFor(o => o.YourEnumFieldName))') {
$(this).attr("selected", "selected");
return;
}
});
Ok, so from what I could see the problem was caused by using an ActionLink to pass back the full model of an item being edited. Everything was being sent back in a Query string, so my Enum values were being passed to the controller in the following way: mkEnum=Mk4.
I was then loading the UpdateCar view as seen above in my example - but the query string values were being persisted in the call back to the View.
EnumDropDownListFor is unable to interpret/convert the text value of enums into their actual values - if I manually edited the Query string to mkEnum=1, then the correct value wasloaded into the ViewModel.
In addition to this problem, it was not a good solution passing the full model back to the controller.
I've modified the code to pass back a single Id of the item being edited - the controller then verifies the user has access to that Id, retrieves the Model from the Database then passes it back to the same View as in my above example.
With this change my dropdowns are now being updated with their values without any issues.
TLDR; If you experience this issue check to make sure you don't have model properties, specifically enum values represented by their string values, in a query string when loading your view and using EnumDropDownListFor.
I have created the custom "DataType" annotation to use on the model object to tell this is the date field on the view. ([DataType("Date")]) If I use #Html.EditorFor(model => model.DateCreated), it will act as a date field and pup up the JavaScript date picker. This is the template I am using under EditorTemplates
#inherits System.Web.Mvc.WebViewPage<DateTime>
<div class="input-append date" data-date="12-02-2012">
<input type="text" class="span2">
</div>
View -
<div class="control-group">
#Html.LabelFor(model => model.DateCreated, new { #class = "control-label" })
<div class="controls" id="date-container">
#Html.EditorFor(model => model.DateCreated, new { #class="input-append date"})
#Html.ValidationMessageFor(model => model.DateCreated, null, new { #class = "help-inline" })
</div>
</div>
Model -
[Display(Name = "Date Created")]
[DataType("Date")]
public DateTime DateCreated { get; set; }
Controller -
public ActionResult Create(int id)
{
// Attempt to get new customer object
GetPaymentResponse paymentResponse = _BillingService.GetPayment(id);
// Check whether response was successful
if (paymentResponse.State == BaseResponseState.Valid)
{
paymentResponse.Payment.Type.AvailableOptions = paymentResponse.Payment.Type.AvailableOptions.Where(x => x.Value != "BalancingTransaction").ToList();
ViewData.Model = paymentResponse.Payment;
return View();
}
}
I need to pass some additional value to my view via datatype from the model.
E.g. [DataType("Date"), Format("dd/mm/yy"), StartDate("12-02-2012")]
Could you please let me know how can I grab these additional value from the template? (I am new to ASP.Net MVC and I am using MVC 3)
Thanks
If you specify extra information using attributes, the information must be constant for all the instances of the class in which you define the member. I.e. the StartDate will be the same for all instances of your model, becasue the start date specified in the attribute must be a constant.
If that serves your purpoes, you can use a custom metadata provider to get specific metadata in your model from your custom attributes.
If you need to pass different data fro each case, you have to use any of the overloads of EditorFor which allows to pass extra view data. Then you can read that extra information from the ViewData in your template.
Be warned that there are some caveats in the metadata providers and custom template implementations, and registration. Take into account if your type can be made nullable, like DateTime?
If you use the Model Binding it should take the value properly I believe.
In your view, set the model on the first line, line this:
#model MyViewModel
and then in your controller, instead of passing the Model through the ViewData, do something like this:
var model = new MyViewModel();
// do stuff with your model here
return View(model);
Assuming that StartDate is a property of Payment
<div class="control-group">
#Html.LabelFor(model => model.StartDate, new { #class = "control-label" })
<div class="controls" id="date-container">
#Html.EditorFor(model => model.StartDate, new { #class="input-append date"})
#Html.ValidationMessageFor(model => model.StartDate, null, new { #class = "help-inline" })
</div>
</div>
I am new to MVC and have some difficulties understanding this.
To make it simple, I have a "Person" object and this object has an IEnumerable property called "EmailaddressList".
I have generated an edit page through Visual Studio 2012. The main objects properties, are generated on the edit page with textboxes like Name and LastName.
However the list of e-mail addresses in the IEnumerable list of sub-objects are not generated automatically in my view.
This is OK, I have written that code by hand using a tab for each type of e-mailaddress.
So far so good.
Problem:
When I recieve the model (person object) in my HTTP-Post method, the EmailAddressList is null.
Why is it like this, It was not null when I sent it to the view.
I the tab where the e-mailadresses are listed is in a partial view.
Can anyone give me some tips, is it something I'm missing here?*
View-Code
<div id="tabs">
<ul>
#foreach (var item in Model.EmailAddressList)
{
<li>#Html.Label(item.AddressType)</li>
}
</ul>
#foreach (var item in Model.EmailAddressList)
{
<div id="#item.AddressType">
<p>
#Html.TextBoxFor(s => item.EmailAddress, new { #class = "input-xxlarge" })
</p>
</div>
}
</div>
Controller (recieving method)
Here person.EmailAddressList is null
[HttpPost]
public ActionResult Create(Person person)
{
if (ModelState.IsValid)
{
personRepository.InsertOrUpdate(person);
personRepository.Save();
return RedirectToAction("Index");
}
else
{
return View();
}
}
That's because in order to correctly index your fields (so model binder can do it's work), you have to use a for loop.
First, change your IEnumerable to be a List (so we can use an indexor in the view).
Then change your foreach to be the following for loop:
#for (int i = 0; i < Model.EmailAddressList.Count; i++)
{
<div id="#Model.EmailAddressList[i].AddressType">
<p>
#Html.TextBoxFor(m => m.EmailAddressList[i].EmailAddress, new { #class = "input-xxlarge" })
</p>
</div>
}
Based on your update, the reason this doesn't work is because the default model binder only relies on order for a collection of simple data. When it comes to complex type you need to provide the relevant index per item otherwise it doesn't know which item property your referring to e.g.
#for (int i = 0; i < Model.EmailAddressList.Count; i++) {
Html.TextBoxFor(m => m.EmailAddressList[i].EmailAddress) %>
}
See Phil Haack's article on model binding to a list.
It's due to your elements not being ID'd the correct thing for MVC to pick them up on the post back, what you need is:
#Html.EditorFor(model => model.EmailAddressList);
Then, please refer to my post located here on how to make this look to how you want it to.
So, I am trying to create a DropDownList that will edit the various member variables the DateTime class has (which I have an instance of in my model), such as Day, Month, and Year. However, when an item is selected in the DropDownList and the Save input button is clicked, the data does not save. All other edited pieces of data will be changed and saved, but the DateTime field will just not update. I'd rather not make a new model just for my Dates, but it can be done. I can create the SelectList, I do so in an HTML Helper, shown below:
namespace ErrorReport.Helpers
{
public class DateList
{
public static IEnumerable<SelectListItem> DayList
{
get
{
var days = new List<SelectListItem>();
for (int i = 1; i < 32; i++)
{
days.Add(new SelectListItem
{
Value = i.ToString(),
Text = i.ToString()
});
}
return days;
}
}
There's obviously two more Lists that get made, one for Year and one for Month (I'm not bothering with hours, minutes, or seconds), didn't show them since the code is identical. In my View, my editor code looks like the below, and the variable I want to change is CmpD (of DateTime class):
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")"
type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")"
type="text/javascript"></script>
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Edit Report: "#Model.Title"</legend>
#Html.HiddenFor(model => model.ReportId)
#Html.HiddenFor(model => model.SbmD)
#Html.HiddenFor(model => model.UserName)
#Html.HiddenFor(model => model.CmpD)
...irrelevant editor code...
<div class="editor-label">
#Html.LabelFor(model => model.CmpD, "Estimated Completion Date:")
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.CmpD.Month, DateList.MonthList)
#Html.DropDownListFor(model => model.CmpD.Day, DateList.DayList)
#Html.DropDownListFor(model => model.CmpD.Year, DateList.YearList)
//#Html.ValidationMessageFor(model => model.CmpD.Month)
//Above line was commented out
</div>
<p>
<input type="submit" value="Save"/>
</p>
</fieldset>
}
I did have validation in place for CmpD's member variables earlier (only the ones I wanted to change), commented them out because they kept throwing validation errors for every possible SelectList choice (marked in above code). I didn't put arguments in the BeginForm function because they caused save problems with saving the other data. I also added the HiddenFor field for CmpD (which is my DateTime) to get everything to save properly, since without that line of code the return controller did not recognize the Model as valid and didn't save it. I tried adding a HiddenFor field for every member variable in the DateTime class I am not using, and I still get Validation errors if I remove the HiddenFor(model => model.CmpD), even with other Hidden Fields present. I have also tried to make a list this way:
public static IEnumerable<int> YearList
{
get
{
var years = new List<int>();
for (int i = 0; i < 4; i++)
{
years.Add(i);
}
return years.AsEnumerable();
}
}
However, this prevents the Html.DropDownListFor function from working at all, it apparently has to use SelectList items. I have noticed that only strings can be SelectList items, and that the DateTime member variables are ints. Is that causing the problem? Because I don't see where they are recasted to ints and cannot figure out how I would do that. Basically, how do I edit DateTime member variables in a DropDownList?
Also: Is this too much inline code? Thanks in advance!
The DateTime properties (Day, Month, etc.) are all read-only. Hence, it is not possible to set them on a DateTime instance.
You would need settable int properties in your model to be able to update the values from the drop-downs. Then you can construct a DateTime from those values later. I would probably create a separate model class just for this purpose and implement the date editor as separate editor template or partial view that handles all the details (to keep your code clean).