How do I display a collection of editable entities in MVC3 and process them on postback to the Action? - asp.net-mvc-3

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.

Related

Print a value which is later known in the view in Razor

Is there in Razor a way to print some HTML on the page
while the value later known in the view?
For example: I would like to print the sum of an expensive calculation, and that sum should be before the items in the HTML. But with the example below, it will always print 0.
I would like to calculate the sum only once.
I would like to solve this in the view, not in a csharp helper or on the client side (css, javascript etc)
#{
var sum = 0;
}
<table>
#* I would like to print here the sum which is known after the foreach loop*#
<tr><td>total: #sum</td></tr>
#foreach (var item in items)
{
<tr>
#{
var x= item.expensiveCalculation();
sum+= x;
}
//print item with x
<td>#x</td>
</tr>
}
</table>
edit: It is very important that the expensiveCalculation() is only calculated once for each item!
Your model is not adapted to the requirements of the view. Full stop.
So when your model is not adapted to the requirements of your view you go ahead and define a view model so that all the expensive operations are done inside the controller.
So:
public class ItemViewModel
{
public decimal Price { get; set; }
public string Name { get; set; }
}
Now your view becomes strongly typed to the view model and there are no longer expensive operations there:
#model IEnumerable<ItemViewModel>
<table>
<tr>
<td>
total: #Model.Sum(item => item.Price)
</td>
</tr>
#foreach (var item in Model)
{
<tr>
<td>#item.Name - #item.Price<td>
</tr>
}
</table>
and now inside your controller action prepare this view model and pass it to the view:
public class SomeAction()
{
IEnumerable<Item> items = ... go and fetch your domain models from wherever you are fetching them
// now let's build the view model
var model = new MyViewModel
{
Items = items.Select(x => new ItemViewModel
{
Name = x.Name,
Price = x.expensiveCalculation()
})
};
// and we are obviously passing the view model to the view, not the domain model
return View(model);
}
As you can see we are doing the expensive operations for each element inside the controller in order to bind it to the corresponding property of the view model (Price) but we are no longer paying this expensive operations price inside the view as we are simply summing over the view model pre-calculated properties.
And next time when you encounter a problem in ASP.NET MVC don't forget that view models are the solution to your problem.

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.

ASP.Net MVC Razor Remove Time From DateTime Property

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.

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