ASP.Net MVC Razor Remove Time From DateTime Property - asp.net-mvc-3

I am developing an ASP.Net MVC 3 Web application using Razor Views. I have the following ViewModel which is passed to my Razor View and iterated through to display a list of records.
ViewModel
public class ViewModelLocumEmpList
{
public IList<FormEmployment> LocumEmploymentList {get; set;}
}
View
<table>
<tr>
<th>Employer</th>
<th>Date</th>
</tr>
#foreach (var item in Model.LocumEmploymentList) {
<tr>
<td>#item.employerName</td>
<td>#item.startDate</td>
</tr>
}
</table>
My problem is that the line
#Html.DisplayFor(modelItem => item.startDate)
Returns a date like this 20/06/2012 00:00:00, and I would like it to remove the time and just display the date, ie, 20/06/2012.
I have tried adding
#Html.DisplayFor(modelItem => item.startDate.Value.ToShortDateString())
And
DisplayFor(modelItem => item.startDate.HasValue ? item.startDate.Value.ToShortDateString(): "")
However, they both return the following error message at runtime
Templates can be used only with field access, property access, single-dimension array index, or single-parameter custom indexer expressions.
I have looked at Darin Dimitrov’s answer here Converting DateTime format using razor
However, I don’t have access to the startDate property in my ViewModel, my ViewModel returns an IList of FormEmployment objects which you can see above.
If anyone has any idea’s on how to remove the time from the date time property then I would be greatly appreciative.
Thanks.
Also, my startDate property is Nullable.
Update
Based on PinnyM's answer, I added a partial class (see below) to place the [DisplayFormat] attribute on the startDate property.
public partial class FormEmployment
{
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}")]
public Nullable<System.DateTime> startDate { get; set; }
}
However, my Razor View still displays 20/06/2012 00:00:00 using the following code
#Html.DisplayFor(modelItem => item.startDate)
Any idea's?
Thanks.

You can use #item.startDate.Value.ToShortDateString() (adding the proper validation for null value)

You can use a DisplayFormat attribute on your model startDate property:
[DisplayFormat(DataFormatString="{0:dd/MM/yyyy}")]
public DateTime? startDate { get; set; }
The just use DisplayFor(modelItem => item.startDate)
Another option is to create a read-only property just for the formatting:
public String startDateFormatted { get { return String.Format("{0:dd/MM/yyyy}", startDate); } }
And use DisplayFor(modelItem => item.startDateFormatted)

For me I was able to do something similar to the above answer, but using "Value" attribute kept giving errors.
<td>
#item.DOB.ToShortDateString()
</td>
Other things to note: I'm using ASP.Net Core MVC with the .NET 5 framework so not sure how that's classified or called these days.
Hope this helps someone else coming across this issue later in the future.

Related

MVC3 - Viewmodel with list of complex types

Apologies if this has been asked before; there are a million ways to phrase it so searching for an answer has proved difficult.
I have a viewmodel with the following properties:
public class AssignSoftwareLicenseViewModel
{
public int LicenseId { get; set; }
public ICollection<SelectableDeviceViewModel> Devices { get; set; }
}
A simplified version of SelectableDeviceViewModel would be this:
public class SelectableDeviceViewModel
{
public int DeviceInstanceId { get; set; }
public bool IsSelected { get; set; }
public string Name { get; set; }
}
In my View, I am attempting to display a list of editable checkboxes for the Devices property, inside an input form.
Currently, my View looks like this:
#using (Html.BeginForm())
{
#Html.HiddenFor(x => Model.LicenseId)
<table>
<tr>
<th>Name</th>
<th></th>
</tr>
#foreach (SelectableDeviceViewModel device in Model.Devices)
{
#Html.HiddenFor(x => device.DeviceInstanceId)
<tr>
<td>#Html.CheckBoxFor(x => device.IsSelected)</td>
<td>#device.Name</td>
</tr>
}
</table>
<input type="submit" value="Assign" />
}
The problem is, when the model gets posted back to the controller, Devices is null.
My assumption is that this is happening because even though I'm editing its contents, the Devices property is never explicitly included in the form. I tried including it with HiddenFor, but that just resulted in the model having an empty list instead of null.
Any idea what I'm doing wrong here?
My assumption is that this is happening because even though I'm
editing its contents, the Devices property is never explicitly
included in the form.
No, your assumption is wrong. The reason this doesn't get bound properly is because your input fields doesn't have correct names. For example they are called name="IsSelected" instead of name="Devices[0].IsSelected". Take a look at the correct wire format that needs to be used to bind to collections: http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx
But why this happens?
It happens because of the foreach loop that you used in your view. You used x => device.IsSelected as lambda expression for the checkbox but this doesn't take into account the Devices property at all (as you can see by looking at the generated source code of your web page).
So what should I do?
Personally I would recommend you using editor templates as they respect the navigational context of complex properties and generate correct input names. So get rid of the entire foreach loop in your view and replace it with a single line of code:
#Html.EditorFor(x => x.Devices)
and now define a custom editor template that will automatically be rendered by ASP.NET MVC for each element of the Devices collection. Warning: the location and name of this template are very important as this works by convention: ~/Views/Shared/EditorTemplates/SelectableDeviceViewModel.cshtml:
#model SelectableDeviceViewModel
#Html.HiddenFor(x => x.DeviceInstanceId)
<tr>
<td>#Html.CheckBoxFor(x => x.IsSelected)</td>
<td>#Html.DisplayFor(x => x.Name)</td>
</tr>
Another approach (which I don't recommend) is to change your current ICollection in your view model to an indexed collection (such as an IList<T> or an array T[]):
public class AssignSoftwareLicenseViewModel
{
public int LicenseId { get; set; }
public IList<SelectableDeviceViewModel> Devices { get; set; }
}
and then instead of the foreach use a for loop:
#for (var i = 0; i < Model.Devices.Count; i++)
{
#Html.HiddenFor(x => x.Devices[i].DeviceInstanceId)
<tr>
<td>#Html.CheckBoxFor(x => x.Devices[i].IsSelected)</td>
<td>#Html.DisplayFor(x => x.Devices[i].Name</td>
</tr>
}
EditorFor templates work and keep the code clean. You don't need the loops and the model is posted back correctly.
However, has anyone had problems with validation on complex viewmodels (nested EditorFor templates)? I'm using Kendo Validator and am running into all sorts of jquery errors.

How do I display a collection of editable entities in MVC3 and process them on postback to the Action?

I have a collection of entities, called Accounts. A single user will have multiple Accounts and can set percentages for each Account.
The UI will list all the Accounts, the editable field for each Account is the Percentage property. This will be represented by a textbox.
Once the user makes any adjustments, they will post the form back for processing.
I have used MVC in a few other projects and I understand model binding, but I am not sure how to proceed when dealing with a collection of models.
I am not looking for code or for someone to write out the complete answer for me (although, honestly I wouldn't complain....). I am looking for guidance in the direction to take.
Thanks in advance.
Two things:
1) You'll want to use a List/Collection of Account or List/Collection of AccountViewModel in your overarching page view model
So, you would have
public class MyPageViewModel
{
public Collection<Account> AccountList;
//AccountViewModel is what I'd use but for simplicity
//Other properties for view model
}
You now have a view model that contains a list of accounts.
2)
In your view you have two options.
a) Just create a for loop to spit out the HTML you want for each account (not especially pretty)
b) Create an editor template so you don't have to do anything special
Your page would then have this.
#Html.EditorFor(model => model.AccountList)
Here's how editor templates are set up.
This is from a project I'm working on right now. The take away here should be that you add an "EditorTemplates" folder under the view folder that matches what you're working on. By convention, MVC will look in this folder for a template when you use EditorFor. Now you just insert whatever HTML you want in that strongly typed template (which would take an Account or AccountViewModel object) and you'd be done. No need for a for loop in your view to spit out HTML for each account.
Really, that would be it at that point. Your post action would model bind to a type of MyPageViewModel which would have the updated data you wanted
Hope that gets you pointed in the right direction.
Here is an example of how to solve your task
First lets define a model:
public class Account
{
public int Id { get; set; }
public string Number { get; set; }
public decimal Percentage { get; set; }
}
public class UserEditModel
{
public ICollection<Account> Accounts { get; set; }
}
UserEditModel is a view model. It contains property Accounts declared as collection of Account.
I'll describe a table in a view to show accounts and edit their percentages:
<form action="Url.Action("Save")" method="POST">
<table>
<thead>
<tr>
<td>Number</td>
<td>Percentage</td>
</tr>
</thead>
<tbody>
#for (var i = 0; i < Model.Accounts.Count(); i ++)
{
<tr>
<td>
#Html.HiddenFor(m => m.Accounts[i].Id)
#Html.HiddenFor(m => m.Accounts[i].Number)
#Model.Accounts[i].Number
</td>
<td>
#Html.TextBoxFor(m => m.Accounts[i].Id)
</td>
</tr>
}
</tbody>
</table>
</form>
After submitting the form in Accounts property of the view model we'll have changed by user values of percentages.

Losing data in models and collections inside the ViewModel on postback

I am using a viewmodel that contains a few other models(entities) for each partial view.
I am rendering a partial view by passing the entity which is inside the ViewModel. My partial view has a few fields and some buttons. On click of button (which is inside my partial view) the form is being posted back with the data in a sub entity, whereas my viewmodel is always posted back as null...
I need the data to be present in my viewmodel on post back.
All views are strongly typed:
Code:
public class OrdersVM
{
public FiltersVM filterCriteria { get; set; }
public IEnumerable<MeterInventory> meters { get; set; }
public string assignTo { get; set; }
public IEnumerable<SelectListItem> AssigneeOptions { get; set; }
}
public partial class Meters
{
public int MTRNO { get; set; }
public string LOCName { get; set; }
}
public class FiltersVM
{
public string Center { get; set; }
public DateTime? DueDate { get; set; }
}
View Code
#model OrdersVM
#{
ViewBag.Title = "Orders";
}
#using (Html.BeginForm())
{
<div>
#Html.Partial("~/Views/Base/Filters.cshtml", Model.filterCriteria)
</div>
#foreach (var item in Model.meters)
{
<table>
<tr>
<td>
#Html.Encode(item.LOCNAME)
</td>
</tr>
</table>
}
}
Controller code
[HttpPost]
public ActionResult Index(OrdersVM orders, FiltersVM filters)
{
//orders is null
//filters has values
}
Thanks Olivehour. I am using the partial view "Filters.cshtml". and am rendering the same.
Below is the code for partial view :
#model ViewModels.FiltersVM <fieldset>
<legend>Order Assignment</legend>
<table id="tbl1" class="tableforcontrols">
<tr>
<td>
<div class="editor-label">
#Html.LabelFor(model => model.LDC)
</div>
</td>
<td>
<div class="editor-field">
<input type="submit" value="Search" id="btnSearch" name="button" />
</div>
</td>
<td>
<div class="editor-field">
<input type="submit" class="cancel" value="Reset" id="btnReset" name="button" />
</div>
</td>
</tr>
</table> </fieldset>
I tried with single argument "OrdersVM" (parent view model) but no luck.
[HttpPost]
public ActionResult Index(OrdersVM orders)
but if I pass the parent viewmodel to the partial view it was holding the data in OrdersVM.filterCriteria but not for properties (IEnumerable meters, string assignTo and Enumerable AssigneeOptions)
#Html.Partial("~/Views/Base/Filters.cshtml", Model)
I am new to MVC. Please let me know if any one finds the solution.
Thanks in advance.
It looks like you have a couple of problems here. One probable reason why the orders arg is null in your action method is because it doesn't look like you are rendering any input elements. You just have #Html.Encode(item.LOCNAME).
In order for the default model binder to construct an instance of OrdersVM and pass it to the action method, it needs to have input from the HTTP POST. You need something more like #Html.TextBoxFor(m => item.LOCNAME).
The second problem I think is that you have 2 arguments in the action method. Since the OrdersVM already has a FiltersVM property, you should just be able to have a single OrdersVM argument to the action method. During the HTTP POST, you can just access FiltersVM properties from OrdersVM.filterCriteria. This will lead to your 3rd challenge, though, since the meters property on OrdersVM is an IEnumerable collection.
To solve this, first have a couple reads of this article about model binding collections. It's old, but it still applies in MVC3. Read it and really wrap your head around it.
If you don't like using integers to index your collection fields, there is an HTML helper written by Steve Sanderson that allows you to index collection inputs using GUID's. We use this all the time, but it can be tricky -- mainly, you should always put the collection item in a partial view. For now, you might just be better off using integer-based indexing as outlined in the Haacked article.
It sounds like you are comming from Webforms. To transition to MVC you need to remove the thought of PostBack. This is concept that doesn't really exist on the web but Webforms faked it for us.
In MVC you usually start with a GET request like /edit/{someId}. From here you load the data for the viewmodel from the database and render the view. Now let's say that all data in the viewmodel is editable so each property have it's own input field. The user edits some data and saves the form. This issues a POST to the server.
Assume we have this POST method
[HttpPost]
public ActionResult Edit(MyViewModel model)
In this case you have all the data you need modelbinded because all data existed in the form.
You could do this and get the same view rendered because all data was databinded.
[HttpPost]
public ActionResult Edit(MyViewModel model){
return View(model);
}
Now let's pretend you have a dropdown in your form. Then you would have these two properties in your viewmodel.
public int CarId { get; set; }
public IEnumerable<SelectListItem> CarOptions {get; set; }
When you post the form this time the CarId will be populated in the ViewModel but not CarOptions because they are not a part of the form data. What you do if you would want to return the same view again is to reload the missing parts.
[HttpPost]
public ActionResult Edit(MyViewModel model){
model.CarOptions = LoadCarOptions();
return View(model);
}
It's certainly possible to modelbind that too if you put it in a hidden field. But it's easier and probably more effective to reload it from server/database again. This is the normal approach taken when working with MVC.

Using ASP.NET MVC 3 with Razor, what's the most effective way to add an ICollection to a Create view?

I'm using Entity Framework Code First to generated my database, so I have an object defined like the following:
public class Band
{
public int Id { get; set; }
[Required(ErrorMessage = "You must enter a name of this band.")]
public string Name { get; set; }
// ...
public virtual ICollection<Genre> Genres { get; set; }
}
Now I'm looking at a create view for this and the default scaffolding isn't adding Genres to my form, which from past experience is about what I expect.
Looking online I've found Using ASP.NET MVC v2 EditorFor and DisplayFor with IEnumerable<T> Generic types which seems to come closest to what I want, but doesn't seem to make sense with Razor and possibly MVC 3, per ASP.NET MVC 3 Custom Display Template With UIHint - For Loop Required?.
At present I've added the listing of genres to the ViewBag and then loop through that listing in my create view:
#{
List<Genre> genreList = ViewBag.Genres as List<Genre>;
}
// ...
<ul>
#for (int i = 0; i < genreList.Count; i++)
{
<li><input type="checkbox" name="Genres" id="Genre#(i.ToString())" value="#genreList[i].Name" /> #Html.Label("Genre" + i.ToString(), genreList[i].Name)</li>
}
</ul>
Outside of not yet handling cases where the user has JavaScript disabled and the checkboxes need to be re-checked, and actually updating the database with this information, it does output the genres as I'd like.
But this doesn't feel right, based on how good MVC 3 has become.
So what's the most effective way to handle this in MVC 3?
I don't send lists into my View via the ViewBag, instead I use my viewmodel to do this. For instance, I did something like this:
I have an EditorTemplate like this:
#model IceCream.ViewModels.Toppings.ToppingsViewModel
<div>
#Html.HiddenFor(x => x.Id)
#Html.TextBoxFor(x =x> x.Name, new { #readonly="readonly"})
#Html.CheckBoxFor(x => x.IsChecked)
</div>
which I put in my Views\IceCream\EditorTemplates folder. I use this to display some html for allowing the user to "check" any particular topping.
Then in my View I've got something like this:
#HtmlEditorFor(model => model.Toppings)
and that will use that result in my EditorTemplate being used for each of the toppings in the Toppings property of my viewmodel.
And then I've got a viewmodel which, among other things, includes the Toppings collection:
public IEnumerable<ToppingsViewModel> Toppings { get; set; }
Over in my controller, among other things, I retrieve the toppings (however I do that in my case) and set my viewmodel's property to that collection of toppings. In the case of an Edit, where toppings may have been selected previously, I set the IsChecked member of the TopingsViewModel and it'll set the corresponding checkboxes to checked.
Doing it this way provided the correct model binding so that when the user checked a few toppings, the underlying items in the collection reflected those selections. Worked well for me, hope it's helpful for you.

When do I use View Models, Partials, Templates and handle child bindings with MVC 3

new to mvc3, i have a few questions, would appreciate if someone could answer/provide links:
When should I use View Models? Is it not recommended to use the domain? I find that my view models are copies of my domain objects, and don't see the value...
When should I use Partials? Is it only if the partial view will be reused?
When should I use Display Templates and Editor Templates? Can I use these without a view model?
How do I create an edit screen where the parent and list of child objects are both editable? i.e. a few fields at the top (parent) and a grid of fields below (like editable rows), particularly, how do i do the binding? automapper is not used.
Thanks!
When should I use View Models? Is it not recommended to use the domain? I find that my view models are copies of my domain objects, and don't see the value...
View models should always be used. You should not use your domain models in the view.
View models are not exact copies of the domain models. They always have some differences related to the specific requirements of the view. For example on one screen you would like to present some of the properties of your domain model and on other screen other properties. As a consequence to this you will also have different validation requirements as one property will be required on one screen and not required on other screen. So you will also have different data annotations on those view models.
When should I use Partials? Is it only if the partial view will be reused?
Not only if the view will be reused. Partials could be used to make your views more structured. Also if you are using AJAX, partials make it easier. You would send the AJAX request to a controller action which will return a partial view allowing you to update only portions of the DOM.
When should I use Display Templates and Editor Templates? Can I use these without a view model?
Always. You can use them with any strongly typed model, but you should always use a view models (see answer to previous question).
How do I create an edit screen where the parent and list of child objects are both editable? i.e. a few fields at the top (parent) and a grid of fields below (like editable rows), particularly, how do i do the binding? automapper is not used.
That's a pretty broad question but to answer it as always you start with defining your view models which will represent/contain the properties you would like to present on this screen for editing:
public class ChildViewModel
{
[Required]
public string Name { get; set; }
}
public class ParentViewModel
{
[Required]
public string Name { get; set; }
public IEnumerable<ChildViewModel> Children { get; set; }
}
then a controller:
public class HomeController: Controller
{
public ActionResult Index()
{
// TODO: Fetch an actual domain model from your repository,
// and map it to the view model (AutoMapper is a great tool for the job)
var model = new ParentViewModel
{
Name = "parent name",
Children = Enumerable.Range(1, 5).Select(x => new ChildViewModel
{
Name = "child " + x
})
};
return View(model);
}
[HttpPost]
public ActionResult Index(ParentViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
// TODO: the model object here will contain the new values
// => user AutoMapper to map it back to a domain model
// and pass this domain model to the repository for processing
return RedirectToAction("Index");
}
}
and finally the view:
#model ParentViewModel
#using (Html.BeginForm())
{
<h2>Parent</h2>
<div>
#Html.LabelFor(x => x.Name)
#Html.EditorFor(x => x.Name)
#Html.ValidationMessageFor(x => x.Name)
</div>
<h2>Children</h2>
<table>
<thead>
<tr>
<th>Child name</th>
</tr>
</thead>
<tbody>
#Html.EditorFor(x => x.Children)
</tbody>
</table>
<input type="submit" value="OK" />
}
and the last piece is the editor template for a child (~/Views/Home/EditorTemplates/ChildViewModel.cshtml):
#model ChildViewModel
<tr>
<td>
#Html.LabelFor(x => x.Name)
#Html.EditorFor(x => x.Name)
#Html.ValidationMessageFor(x => x.Name)
</td>
</tr>

Resources