-1

I have a question on representing complex views on a View. Since my code is already complex, I have come up with a simple model for purposes of seeking an answer.

Consider this as my model class and the complex type

public class Person
{
    public string FirstName { get; set; }
    public string Surname { get; set; }
    public string Phone { get; set; }
    public List<Address> Address {get; set;}
}

[ComplexType]
public class Address
{
    public string Street { get; set; }
    public string City { get; set; }
    public string State { get; set; }
}

If for instance I want to use html helpers for displaying a TextBox for FirstName then this would work

@Html.TextBoxFor(model => model.FirstName, new { @class = "Name" })

I have a problem now rendering say a Textbox for Address' Street property. I don't want to hard code it into the view and I would really prefer using a Lambda expression to get a text box for say street, city and state. Though this syntax is wrong, here is what am trying to accomplish

@Html.TextBoxFor(model => model.Address.Street, new { @class = "Name" })

Or something legal in C# that would abstract me from typing names for input controls right into helper methods. This is because while using Model Binding for my project, I don't want trips back to my Views to hard code an inputs name once my model changes.

UPDATE Address is a List type. I have updated the code to reflect the fact that Address is a List of type Address in my code.

Dev
  • 1,146
  • 2
  • 19
  • 30
  • 1
    Have you tried `@Html.EditorForModel`? See http://stackoverflow.com/questions/6537220/why-not-use-html-editorformodel. – John Saunders May 19 '15 at 18:38
  • The example you gave with `Html.TextBoxFor(m => m.Address.Street)` looks valid to me. Did it not work for you? Maybe I'm misunderstanding your question. – Peter May 19 '15 at 18:43
  • `Html.TextBoxFor(m => m.Address.Street)` doesn't work for me. I have edited my question to reflect something I believe is what is the issue. Let Address be a List
    – Dev May 19 '15 at 18:53

2 Answers2

1

With MVC, you can set up the view to expect a specific model. If you do this, you get full Intellisense on the model, no matter how complex. I prefer this to dumbing down all of the models to strings to make it "easier" to bind.

Here is a quick post I found on strongly typed views: http://www.c-sharpcorner.com/UploadFile/abhikumarvatsa/strongly-typed-views-in-mvc/

See if gives you some ideas on progressing.

Gregory A Beamer
  • 16,870
  • 3
  • 25
  • 32
0

You can use either a custom EditorTemplate for typeof Address or use a for loop.

In /Views/Shared/EditorTemplates/Address.cshtml

@model yourAssembly.Address
@Html.TextBoxFor(m => m.Street, new { @class = "Name" })
@Html.TextBoxFor(m => m.City, new { @class = "Name" })
....

and in the main view

@Html.EditorFor(m => m.Address)

or using a for loop in the main view

for(int i = 0; i < Model.Address.Count; i++)
{
    @Html.TextBoxFor(m => m.Address[i].Street, new { @class = "Name" })
    @Html.TextBoxFor(m => m.Address[i].City, new { @class = "Name" })
    ....
}

both options will generate the correct name attributes so that your collection will be bound on post back, for example

<input name="Address[0].Street" .../>
<input name="Address[1].Street" .../>

Side note: Suggest you use plural naming for collections - public List<Address> Addresses { get; set; } - so it more obvious that your property is a collection and not a single object

  • Am using that code in my Create view so `Model.Address.Count` wont be applicable. I wanted to use the first suggestion as I had seen in a post somewhere but compiler complained coz of two @model statements in a View – Dev May 20 '15 at 04:24
  • Why do you have 2 @model statements? All you need is `@model yourAssembly.Person` –  May 20 '15 at 04:26
  • And not sure what you mean by _"Model.Address.Count wont be applicable"_? If you are creating a new person then you need to add at least one `Address` object to your collection so you can edit it. If you want to dynamically add multiple `Address` objects in the view then you need an entirely different approach involving javascript/jquery (but there is no mention of that in your question) –  May 20 '15 at 04:30
  • I would have two @model statements since I already have the `@model yourAssembly.Person` at the top. I wanted to use lambda expression functionality for the Address type too and I cannot reference `@model yourAssembly.Address` in the View. This is how it results in two @model statements. – Dev May 20 '15 at 05:06
  • You **don't** include `@model Address` in the view! `Address` is already a property of `Person` (or at least `List
    ` is) You need to read my last comment.
    –  May 20 '15 at 05:13
  • I have followed your advice and the default model binder works ok but the values of the complex type pulled from the database are null. This is despite saving to the db successfully. What is the issue or am I not saving to the db at all? Note that I have the field in the bind attribute. – Dev May 21 '15 at 04:29
  • There is nothing in the code in the question about saving or getting data from the database so impossible to tell what the issue may be. You will need to ask a new question showing the relevant code (controller methods) –  May 21 '15 at 04:38
  • I have used your code in my Create View and I get a NullReference exception on `Model.Address.Count`, just as I had mentioned earlier to you, I didn't think it would be applicable in Create View. – Dev May 21 '15 at 05:37
  • The only thing that could generate that exception is that the property `Address` is null. You need to initialize it in the controller - `model.Address = new List
    ();` or better include a parameterless constructor in `Person` - `public Person() { Address = new List
    (); }` Then you need to add a new `Address` object in the controller for editing it - `model.Address.Add(new Address);`
    –  May 21 '15 at 05:49