Input with date format set from ViewModel and html class attribute - asp.net-mvc-3

I am working with razor view engine in asp.net mvc3.
Now, I need an input for a DateTime which should display the value in a fixed format (say dd-MMM-yyyy). So I can do:
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd-MMM-yyyy}")]
public DateTime StartDate { get; set; }
And in the view:
#Html.EditorFor(model => model.StartDate)
But I need to add a class in the input. Which I think is not possible in EditorFor.
So I could use
#Html.TextBoxFor(model => model.StartDate, new { #class = "Date" })
But the display format does not work in this case.
Model can be null. So,
#Html.TextBox("StartDate", string.Format("{0:dd-MMM-yyyy}", Model.StartDate))
will throw NullReferenceException.

ophelia's way is a quicker version of mine. But this way is useful if you're going to have a few date fields in your application. So this is the way i like to do it:
-> In your solution explorer, create a folder inside Shared called "EditorTemplates"
-> In there, add a new (not strongly typed) Partial View. Call it "DateTime".
-> Open this view, and remove any code in there, and throw the following code in there:
#model System.DateTime
#Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue, new { #class = "date" /* + any other html attributes you want */ })
-> In your ViewModel, your date field should be like this:
[Display(Name = "My Date:"), DisplayFormat(DataFormatString = "{0:dd-MMM-yyyy}", ApplyFormatInEditMode = true)]
public DateTime MyDate { get; set; }
-> In your main view, and any view you want to use a date field, you can just use EditorFor on the property, like so:
#Html.EditorFor(m => m.MyDate)
You can initialize the date field in your controller or set a default date in your viewmodel's constructor.

I use this and its works fine:
#Html.TextBox("ExpiryDate", String.Format("{0:ddd, dd MMM yyyy}", DateTime.Now), new { id = "expirydate" })
Hope this is what you mean/need :)

Related

Datetime validation with custom format. Troubles with '.' separator

In my web application (asp.new mvc) I try to make validation for date field.
If I use this
[DataType(DataType.Date)]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd/MM/yyyy}")]
public DateTime? PeriodBeginFrom { get; set; }
or this
[DataType(DataType.Date)]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd-MM-yyyy}")]
public DateTime? PeriodBeginFrom { get; set; }
it works. But if I try to separate using '.' separator
[DataType(DataType.Date)]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd.MM.yyyy}")]
public DateTime? PeriodBeginFrom { get; set; }
I have an issue. jQuery validation says that I have to enter date if I try '01.01.2001'.
Here is view
<div class="ElementDiv">
#Html.LabelFor(m => m.Filter.PeriodBeginFrom, new { #class = "Labels" })
#Html.EditorFor(m => m.Filter.PeriodBeginFrom, new { #class = "TextBoxes" })
#Html.ValidationMessageFor(m => m.Filter.PeriodBeginFrom, null, new { #class = "Errors" })
</div>
Why? With all other separators it works. What a problem is with '.' separatior?
Update 1: It seems that validation does not work properly. If I have this attribute at my model
[DataType(DataType.Date)]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd/MM/yyyy}")]
public DateTime? PeriodBeginFrom { get; set; }
and we pass DateTime object to model, it will be displayed at textbox with '.' like '12.12.2012' and not like I was waiting '12/12/2012'. Hope you'll give me some ideas.
You're looking at a date component. But remember that time components can include seconds and fractional seconds, with a period inbetween. Since dates and times are frequently combined, I would not be surprised if the period is being treated as a special character.
Update:
I went through the following documentation chain:
DisplayFormatAttribute.ApplyFormatInEditMode Property
DisplayFormatAttribute.DataFormatString Property
Formatting Types
Custom Date and Time Format Strings
I expected to see that the period character has a special meaning in the format string. Instead, the documentation doesn't specifically list the period character. That means it should fall in the categry of "Any other character", and therefore, "The character is copied to the result string unchanged."
However, I don't believe it. Fractional seconds occur in time components like 23:59:59.999, and I suspect there is undocumented special treatment of the period character.
Trouble with a format was not with ASP tags and fields but with unobtrusive validation jquery library. I had to overload unobtrusive JavaScript validation method for date for my date format. More details are here
MVC DateTime validation - UK Date format

Single property not getting bound on HttpPost

I'm working on the first MVC3 project at our company, and I've hit a block. No one can seem to figure out what's going on.
I have a complex Model that I'm using on the page:
public class SpaceModels : List<SpaceModel> {
public bool HideValidation { get; set; }
[Required(ErrorMessage=Utilities.EffectiveDate + Utilities.NotBlank)]
public DateTime EffectiveDate { get; set; }
public bool DisplayEffectiveDate { get; set; }
}
In the Controller, I create a SpaceModels object with blank SpaceModels for when Spaces get combined (this would be the destination Space).
// Need a list of the models for the View.
SpaceModels models = new SpaceModels();
models.EffectiveDate = DateTime.Now.Date;
models.DisplayEffectiveDate = true;
models.Add(new SpaceModel { StoreID = storeID, SiteID = siteID, IsActive = true });
return View("CombineSpaces", models);
Then in the View, I am using that SpaceModels object as the Model, and in the form making a TextBox for the Effective Date:
#model Data.SpaceModels
#using (Html.BeginForm("CombineSpaces", "Space")) {
<div class="EditLine">
<span class="EditLabel LongText">
New Space Open Date
</span>
#Html.TextBoxFor(m => m.EffectiveDate, new {
size = "20",
#class = "datecontrol",
// Make this as a nullable DateTime for Display purposes so we don't start the Calendar at 1/1/0000.
#Value = Utilities.ToStringOrDefault(Model.EffectiveDate == DateTime.MinValue ? null : (DateTime?)Model.EffectiveDate, "MM/dd/yyyy", string.Empty)
})
#Html.ValidationMessageFor(m => m.EffectiveDate)
</div>
<hr />
Html.RenderPartial("_SpaceEntry", Model);
}
The Partial View that gets rendered iterates through all SpaceModels, and creates a containing the Edit fields for the individual SpaceModel objects. (I'm using the List to use the same Views for when the Spaces get Subdivided as well.)
Then on the HttpPost, the EffectiveDate is still back at it's DateTime.MinValue default:
[HttpPost]
public ActionResult CombineSpaces(SpaceModels model, long siteID, long storeID, DateTime? effectiveDate) {
// processing code
}
I added that DateTime? effectiveDate parameter to prove that the value when it gets changed does in fact come back. I even tried moving the rendering of the TextBox into the _SpaceEntry Partial View, but nothing worked there either.
I did also try using the #Html.EditorFor(m => m.EffectiveDate) in place of the #Html.TextBoxFor(), but that still returned DateTime.MinValue. (My boss doesn't like giving up the control of rendering using the #Html.EditorForModel by the way.)
There has to be something simple that I'm missing. Please let me know if you need anything else.
Looking at the source code for DefaultModelBinder, specifically BindComplexModel(), if it detects a collection type it will bind the individual elements but will not attempt to bind properties of the list object itself.
What model binding does is attempt to match the names of things or elements in the view to properties in your model or parameters in your action method. You do not have to pass all of those parameters, all you have to do is add them to your view model, then call TryUpdateModel in your action method. I am not sure what you are trying to do with SpaceModel or List but I do not see the need to inherit from the List. Im sure you have a good reason for doing it. Here is how I would do it.
The view model
public class SpacesViewModel
{
public DateTime? EffectiveDate { get; set; }
public bool DisplayEffectiveDate { get; set; }
public List<SpaceModel> SpaceModels { get; set; }
}
The GET action method
[ActionName("_SpaceEntry")]
public PartialViewResult SpaceEntry()
{
var spaceModels = new List<SpaceModel>();
spaceModels.Add(
new SpaceModel { StoreID = storeID, SiteID = siteID, IsActive = true });
var spacesVm = new SpacesViewModel
{
EffectiveDate = DateTime.Now,
DisplayEffectiveDate = true,
SpaceModels = spaceModels
};
return PartialView("_SpaceEntry", spacesVm);
}
The POST action method
[HttpPost]
public ActionResult CombineSpaces()
{
var spacesVm = new SpacesViewModel();
// this forces model binding and calls ModelState.IsValid
// and returns true if the model is Valid
if (TryUpdateModel(spacesVm))
{
// process your data here
}
return RedirectToAction("Index", "Home");
}
And the view
<label>Effective date: </label>
#Html.TextBox("EffectiveDate", Model.EffectiveDate.HasValue ?
Model.EffectiveDate.Value.ToString("MM/dd/yyyy") : string.empty,
new { #class = "datecontrol" })
Sometimes you need to explicitly bind form data using hidden fields such as
#Html.HiddenField("EffectiveDate", Model.EfectiveDate.)
In order to bind the properties of the SpaceModel object you can add individual properties such as SiteID to the view model or add a SpaceModel property for a single SpaceModel. If you want to successfully bind a complex model, add it as a Dictionary populated with key-value pairs rather than a List. You should then add the dictionary to the view model. You can even add a dictionary of dictionaries for hierarchical data.
I hope this helps :)

Telerik MVC DatePicker Doesn't Bind to Model

I am trying to use Telerik MVC DatePicker in my project. Here is the situation:
I have a model which have :
...
[DataType(DataType.Date)]
public System.Nullable<DateTime> EndDate { get; set; }
...
My ProjectCreate view has strong type of the model given above
Between
#using (Html.BeginForm(null, null, FormMethod.Post, new { Class="ym-form ym-columnar" }))
{
...
}
I have:
#(Html.Telerik().DateTimePickerFor(c => c.EndDate)
.Name("EndDatePicker")
Within my action
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult ProjectCreate(ProjectView view)
{
...
}
the
view.EndDate
value is always null whichever date I choose.
I have googled a lot but couldn't find what the problem is.
That's because you are setting the Name of your datepicker. You don't need that when using DatePickerFor:
#(Html.Telerik().DateTimePickerFor(c => c.EndDate))

EditorFor on nullable DateTime - "Nullable object must have a value."

I am attempting to display a form that allows a user to input a new assignment for a person. I'm using a DateTime.cshtml EditorTemplate to handle DateTime values for the assignment. The non-nullable DateTime works fine. The nullable DateTime causes an "InvalidOperationException: Nullable object must have a value."
I have a simple viewmodel that looks like this:
AssignmentViewModel.cs:
public Person Person { get; set; }
public Assignment NewAssignment { get; set; }
Assignment.cs contains:
public DateTime AssignmentStartDate { get; set; }
public DateTime? AssignmentEndDate { get; set; }
My AssignmentController Create() method looks like:
public ViewResult Create(int personId)
{
Person person = personRepository.GetPersonById(personId);
var newAssignment = new AssignmentViewModel { Person = person, NewAssignment = new Assignment() };
return View(newAssignment);
}
My Create.cshtml view looks like this:
#model AssignmentViewModel
#using (Html.BeginForm("Create", "Assignment"))
{
#Html.Hidden("NewAssignment.PersonId", Model.Person.PersonId)
#Html.LabelFor(x => x.NewAssignment.AssignmentStartDate):
#Html.EditorFor(x => x.NewAssignment.AssignmentStartDate.Date, new { cssClass = "datePicker" })
<br />
#Html.LabelFor(x => x.NewAssignment.AssignmentEndDate):
#Html.EditorFor(x => x.NewAssignment.AssignmentEndDate.Value.Date, new { cssClass = "datePicker" })
<br />
<input type="submit" value="Send />
}
My DateTime.cshtml EditorTemplate looks like:
#model DateTime?
#{
String modelValue = "";
if (Model.HasValue)
{
if (Model.Value != DateTime.MinValue)
{
modelValue = Model.Value.ToShortDateString();
}
}
}
#Html.TextBox("", modelValue, new { #class = "datePicker" })
When I attempt to load the Create view, I get the exception mentioned above on the line "#Html.EditorFor(x => x.NewAssignment.AssignmentEndDate.Value)".
You may be wondering why I'm passing in AssignmentEndDate.Value.Date instead of just passing in AssignmentEndDate; the reason is because I'm trying to get to the point where I'm splitting DateTime into Date and a TimeOfDay field and recombine them with a DateTimeModelBinder. I am using a similar technique to the one shown here and here.
I -can- bypass the error, by changing my controller Create() method to instantiate the ViewModel with AssignmentEndDate set to DateTime.MinValue, but this seems completely wrong for a nullable DateTime:
var newAssignment = new AssignmentViewModel
{
Person = person,
NewAssignment = new Assignment { AssignmentEndDate = DateTime.MinValue }
};
Something strange happens after I "bypass" the error by supplying a value for the nullable DateTime; the un-required nullable DateTime property (AssignmentEndDate.Date) fails client side validation. Trying to submit the form highlights the field in red.
How can I handle this correctly?
The problem is that you're trying to retrieve the AssignmentEndDate.Value.Date, but AssignmentEndDate is null, which results in this error.
Since your editor template accepts a DateTime?, you should just pass along the AssignmentEndDate. In other words, remove the .Value.Date from the view:
#Html.EditorFor(x => x.NewAssignment.AssignmentEndDate, new { cssClass = "datePicker" })
Since your editor template is using ToShortDateString(), there's no need to "truncate" the time from the date at all.
Update
Regarding your desire to have separate "Date" and "Time" editors:
You can do this 2 ways.
1 - Your current DateTime? editor renders a field for the Model.Value.Date, so you could simply extend this to also render a field for the Model.Value.TimeOfDay. Example:
#{
DateTime? modelDate = (Model == null) ? (DateTime?)null : Model.Value.Date;
TimeSpan? modelTime = (Model == null) ? (TimeSpan?)null : Model.Value.TimeOfDay;
}
#Html.TextBox(..., modelDate, new{#class="datePicker"})
#Html.TextBox(..., modelTime, new{#class="timePicker"})
2 - You could split the above functionality into 2 separate editors, "DateOnly" and "TimeOnly". Then, update your view to call both editors:
#Html.EditorFor(x => x.NewAssignment.AssignmentEndDate, "DateOnly")
#Html.EditorFor(x => x.NewAssignment.AssignmentEndDate, "TimeOnly")
The choice is up to you, and whether you want to keep the Date and Time parts separate or together, but this is how I'd go about solving this problem.
create a DateTime.cshtml in your Shared/DisplayTemplate folder
#model Nullable<DateTime>
#(Model != null ? string.Format(ViewData.ModelMetadata.DisplayFormatString ?? "{0:d}", Model) : string.Empty)
this supports metadata from datannotations to be used if found.
UPDATE: GetValueOrDefault treats it as a DateTime and therefore the required Field validators are getting attached, because the original expression is for a datetime not a nullable datetime.
Therefore the solution below doesn't work.
Like the asker, I also used the DateTimeModelBinder from here:
Here's the Link
This is how I solved a similar situation:
#Html.EditorFor(x => x.NewAssignment.AssignmentEndDate.GetValueOrDefault().Date)
And this is what my DateTime EditorTemplate looks like:
#model DateTime
#Html.TextBox("", Model != default(DateTime) ? Model.ToShortDateString() : String.Empty, new { #class = "datepicker", #maxlength = "10" })

one property from my ViewModel will not populate ModelMetadata

I'm experiencing very odd behavior in the way an ASP.NET MVC3 view model is emitted -- for one field, ModelMetadata is not propagated. I'm using the templated helpers after Brad Wilson, though updated for Razor. Here's my view model:
public class FamilyBaseViewModel : ViewModelBase
{
[Display(Order = 10)]
public string FamilyName { get; set; }
[Display(Order = 30)]
[StringLength(50, ErrorMessage = "Street name can only be 50 characters long.")]
public string Street { get; set; }
}
public class FamilyPrivateViewModel : FamilyBaseViewModel
{
[Display(Name = "Date Started", Order = 20)]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:d}")]
[DataType(DataType.Date)]
public DateTime? DateStarted { get; set; }
}
The object.cshtml template runs through the properties and uses Html.Display to show them:
// object.cshtml
<ol>
#foreach (var prop in
ViewData.ModelMetadata.Properties.Where(pm => pm.ShowForDisplay
&& !ViewData.TemplateInfo.Visited(pm)
&& pm.ModelType != typeof(System.Data.EntityState)))
{
<li>
#Html.Display(prop.PropertyName)
</li>
}
</ol>
In the above scenario, all three fields have the right descriptors in the object.cshtml call (prop.DisplayName, prop.TemplateHint), but when the first property -- FamilyName -- is passed to String.cshtml, the ViewData.ModelMetadata is not populated at all. As a result, the template can't display a label (except "String"), nor assign the ID of the control, etc.
Street and DateStarted are emitted normally, with the ID and all. So I'm completely at a loss as to why the one property would fail to set the ViewData properties -- nor do I know how to step through past the Html.Display call to see what might be happening.
Any ideas for a next place to look?
So the problem was in the controller action, which for unrelated reasons used "FamilyName" for a ViewData value:
ViewBag.FamilyName = familyName;
And this caused all heck to break loose in the mapping of model fields with the same name -- that is, ModelMetadata will not propagate. So, the lesson is: don't give ViewData dictionary items keys with the same name as a field in your view model.

Resources