MVC2 How To Edit Multiple Linked Customer Addresses - asp.net-mvc

A Customer has a billing and delivery address, so given the following database schema
Customer(CustomerId)
Address(AddressId)
CustomerAddresses(CustomerId,AddressId)
And the following Enitity Framework class
public class Customer
{
public IEnumerable<Address> Addresses { get; set; }
}
I output my input boxes in my view like so
<% foreach (var address in Model.Addresses) { %>
<%: Html.TextBoxFor(model => address.Address1) %>
<% } %>
When I post the form values after entering DeliveryAddress1 and BillingAddress1 and then iterate over the FormCollection keys I get the following value
Customer.address.Address1 =
"DeliveryAddress1,BillingAddress1"
The question is how do I distinguish between the two records?

I would recommend you using editor templates. This way you don't need to write ugly loops in your views and the helpers will take care of generating proper names for the input fields.
So in your main view instead of writing all the code you've shown simply:
<%: Html.EditorFor(x => x.Addresses) %>
And then create an editor template for an address (~/Views/Home/EditorTemplates/Address.ascx)
<%# Control
Language="C#"
Inherits="System.Web.Mvc.ViewUserControl<YourApp.Models.Address>" %>
<%: Html.TextBoxFor(x => x.Address1) %>
Notice the name and location of the editor template. The location should be in the EditorTemplates folder (it could also be in ~/Views/Shared/EditorTemplates/Address.ascx) and the name should be the same as the name of the class (Address). ASP.NET MVC will take care of rendering the template for each element of the Addresses collection of your model.

Related

ASP.NET MVC: Accessing ViewModel Attributes on the view

Is there any way to access any attributes (be it data annotation attributes, validation attributes or custom attributes) on ViewModel properties from the view? One of the things I would like to add a little required indicator next to fields whose property has a [Required] attribute.
For example if my ViewModel looked like this:
public class MyViewModel
{
[Required]
public int MyRequiredField { get; set; }
}
I would want to do something in the EditorFor template like so:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<int?>" %>
<div class="label-container">
<%: Html.Label("") %>
<% if (PROPERTY_HAS_REQUIRED_ATTRIBUTE) { %>
<span class="required">*</span>
<% } %>
</div>
<div class="field-container">
<%: Html.TextBox("") %>
<%: Html.ValidationMessage("") %>
</div>
The information you're looking for is in ViewData.ModelMetadata. Brad Wilson's blog post series on Templates should explain it all, especially the post on ModelMetadata.
As far as the other ValidationAttributes go, you can access them via the ModelMetadata.GetValidators() method.
ModelMetadata.IsRequired will tell you if a complex type (or value type wrapped in Nullable<T>) is required by a RequiredAttribute, but it will give you false positives for value types that are not nullable (because they are implicitly required). You can work around this with the following:
bool isReallyRequired = metadata.IsRequired
&& (!metadata.ModelType.IsValueType || metadata.IsNullableValueType);
Note: You need to use !metadata.ModelType.IsValueType instead of model.IsComplexType, because ModelMetadata.IsComplexType returns false for MVC does not consider to be a complex type, which includes strings.
I would suggest not doing that way because you're adding logic in the view which is a bad practice.
Why don't you create a HtmlHelper or LabelExtension, you can call ModelMetaProvider inside the method and find out whether the property has Required attribute decorated?

ASP.NET MVC - Insert or Update view with IEnumerable model

I've seen plenty of examples (NerdDinner, Sanderson's Sports Store, etc.) where a view is bound to a collection of objects. The syntax in the view is usually something like this...
<%# Page... Inherits="System.Web.Mvc.ViewPage<IEnumerable<MyViewModel>>" %>
...
<% foreach (var myViewModel in Model) { %>
I've also seen plenty of examples of inserts or updates where the controller automatically binds the model parameter to the form elements in the view.
I'm looking for a mix of the two techniques where my view has form elements pertaining to a collection of myViewModels where each myViewModel has 3-4 properties. The intent is to allow the user to enter a set of these in one take.
Assuming this is possible, can anyone help me with the syntax? I can't figure out how to label the form elements to make the binding work.
This is possible through the built-in model binder, but you have to do a little bit of convention-based naming of your form objects. First, your action needs to take a collection:
[HttpPost]
public ActionResult CreateFoos(List<Foo> foos)
{
// I have a list of the foo objects that were posted
}
And then, in the view, let's say you wanted to make a form for each object:
<% for (int i = 0; i < Model.Count; i++) { %>
<%: Html.TextBoxFor(x => x[i].Property1) %>
<%: Html.TextBoxFor(x => x[i].Property2) %>
<%: Html.TextBoxFor(x => x[i].Property3) %>
<% } %>
Pay attention to how the controls are rendered in the HTML, because in your "create" view, you might want to have a javascript button that allows the user to add another record, and you'll have to increase the index for each additional control. It's not too hard, but I just wanted to warn you to pay attention to the source it actually generates.
The definitive answer is here: http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx
You need to name each field as if it were part of an array: "PropertyName[i]"

What's the point of Html.DisplayTextFor()?

Is there a good reason to use the strongly typed html helper...
<%: Html.DisplayTextFor(model => model.Email) %>
As opposed to...
<%: Model.Email %>
Consider the following Model:
public class MyModel
{
public string Name { get; set; }
[DisplayFormat(NullDisplayText = "No value available!")]
public string Email { get; set; }
}
in my view:
<%= Html.DisplayTextFor(m => m.Email) %>
<%: Model.Email %>
The first line will display "No value available" if we leave the Email to be 'null' whereas the second line will not display anything.
Conclusion:
Html.DisplayTextFor will take into consideration the DataAnnotations on your properties, <%: Model.Email %> will not.
Also <%: Model.Email %> will throw an "Object reference error" when the value is null, but <%= Html.DisplayTextFor %> wont.
DisplayTextFor will also get called during "DisplayFor" and "EditFor" execution. This will ensure that any templated helpers will display the text using the correct Templated Helpers if set ... so a change to the single templated helper will propogate through all displays of that text item ... simple display, edit form, create forms etc etc.
Well, DisplayTextFor will not break if you do not pass in a model (or pass null). Model.Email will throw an exception on null. So, DisplayTextFor is more robust.
The point of the strongly typed helpers is to allow greater compile time checking. This is very handy when refactoring.
DisplayTextFor allows using a consistent construct (with the other strongly typed helpers) throughout the page. Some people might find that more appealing.
DisplayTextFor also allows you to pass the names of templates and fields as parameters.

How to format a date in a view for two-way binding in MVC 2?

So let's say I have a view that access a date:
<%= Html.TextBoxFor(model => Model.Birthday) %>
How do I have my view dictate how that date is formatted? I'd like to use common formatting options, and it's important that this is 2-way (both for display and data-entry).
In your model, you could use System.ComponentModel.DataAnnotations metadata to specify the format you want the property in. e.g.
[DisplayFormat(DataFormatString="{0:MM/dd/yyyy}")]
public DateTime Birthday { get; set; }
Then, in your view, use:
<%= Html.EditorFor(model => model.Birthday) %>
Unfortunately, the regular HTML Helpers don't respect the metadata, so you have to use Html.EditorFor.
Use a specific ViewModel class that has a string in the format you want for the birthday and use that to display in the textbox. When you post the form, if you are using default binding for the domain object itself, it should parse the string for you. If you are using a ViewModel, you'll have to parse it upon posting the form.
I was kind of surprised to see that you can't just throw a format string in Html.EditorFor(). I've sinced starting using EditorTemplates.
Create a directory structure in your solution.
Views > Shared > EditorTemplates
Add a new MVC 2 User Control in the EditorTemplates folder and name it DateTime.ascx. Html.EditorFor(DateTime) will now use this user control for display.
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<DateTime?>" %>
<%= Html.TextBox(string.Empty, (Model.HasValue ? Model.Value.ToShortDateString() : string.Empty)) %>

ASP.NET MVC – Database Driven Menu

I have a database menu structure which I would like to add to the site.master file.
I’ve looked at other questions on StackOverflow but cannot get this to work on my website.
How do I add a User Control to the Site.Master file?
Menu.ascx
<%foreach (MainMenuSort mainMenuSort in (List<MainMenuSort>)ViewData["MainMenuSortListDisplay"])
{ %>
<li><%= Html.Encode(mainMenuSort.MainMenuId.MainMenuName)%></li>
<%foreach (SubMenuSort subMenuSort in (List<SubMenuSort>)ViewData["SubMenuSortListDisplay"])
{%>
<%if (mainMenuSort.MainMenuId.Id == subMenuSort.SubMenuId.MainMenu.Id)
{ %>
<li><%= Html.Encode(subMenuSort.SubMenuId.SubMenuName)%></li>
<%} %>
<%} %>
<%}%>
You need to use the Html.RenderPartial method in your master page.
You will need to set the MainMenuSortListDisplay and SubMenuSortListDisplay view data keys in whatever action is calling the view that uses your master page.
In your master use this
<% Html.RenderPartial("~/Views/Shared/Menu.ascx");
The path needs to be the app relative path to the control's folder. Typically these go under Shared. You can make the structure how you want below the Shared folder.
To make this technique stronger, use a strongly typed partial. In the question you would perhaps make a new class (MenuModel) with two generic collections as properties and place it in the models folder of the application. Then in the model's constructor call a method that populates the lists.
public class MenuModel
{
public IEnumerable<MainMenuSort> OuterList {get; set;}
public IEnumerable<SubMEnuSort> InnerList {get; set;}
public MenuModel()
{
VoidThatFillsTheInnerAndOuterList();
}
This will mean that you can do this in your controller
public ActionResult ShowAForm()
{
ViewData["MenuPartialData"] = new MenuModel();
return View();
}
Having set this key, your master page can use the overload of RenderPartial, like this
<% Html.RenderPartial(
"~/View/Shared/Menu.ascx",
(MenuModel)ViewData["MenuPartialData"]); %>
This assumes that your partial is strongly typed to the MenuModel class. Then in the partial you can use the model which rewrites your code slightly
<% foreach (MainMenuSort mainMenuSort in Model.OuterList) { %>
<li><%= Html.Encode(mainMenuSort.MainMenuId.MainMenuName)%></li>
<% foreach (SubMenuSort subMenuSort in Model.InnerList) {%>
<%if (mainMenuSort.MainMenuId.Id == subMenuSort.SubMenuId.MainMenu.Id)
{ %>
<li><%= Html.Encode(subMenuSort.SubMenuId.SubMenuName)%></li>
<%} %>
<%} %>
<%}%>
Hope that helps
Try something like
<% Html.RenderPartial("Menu") %>
EDIT: Corrected a typo
You could also do it as a HTMLHelper and in the MasterPage just call <%= Html.Menu() %>. Then in your HTMLHelper you have the code to get the database records and loop through them. Here is a link I found to get you started. Note my comments as there is a bug in the code example provided. I'm still having issues handling subitems of menus, I guess I need a recursive function or something??
With the help of this link. I was able to display a menu in the site.master page.

Resources