Click here to Skip to main content
15,890,336 members
Articles / Web Development / HTML
Tip/Trick

MVC Dynamic UI for Complex Models

Rate me:
Please Sign up or sign in to vote.
4.31/5 (4 votes)
18 Feb 2016CPOL1 min read 26.2K   258   18   11
Using MVC with complex models - understanding and solving the problem

The Problem

MVC provides a few helper methods for 'automagically' creating UI sections from your model metadata:

HTML
@Html.Editor("Prop")
@Html.EditorFor(mdl => mdl.Prop)
@Html.EditorForModel()
@Html.Display("Prop")
@Html.DisplayFor(mdl => mdl.Prop)
@Html.DisplayForModel()

These methods use a great metadata discovery mechanism to create a proper UI, including server and client side validations. The UI can further be enhanced and customized with an extensive templating system.

Model Complex Properties

The UI generation functions ignore non-primitive properties on your model. You might have an Address property on your Person model that has two string properties City and Street. You might expect the UI to contain two text boxes for those but it does not.

You can, of course, use code like this:

HTML
@Html.EditorForModel()
@Html.EditorFor(m=>m.Child)

You may find it unsatisfactory if you have a complex hierarchy:

HTML
@Html.EditorForModel()
@Html.EditorFor(m => m.Child)
@Html.EditorFor(m => m.Child.GrandChild)
@Html.EditorFor(m => m.Child.GrandChild2)
@Html.EditorFor(m => m.Child2)
@Html.EditorFor(m => m.Child2.GrandChild)
@Html.EditorFor(m => m.Child2.GrandChild2)
@Html.EditorFor(m => m.Child3)
@Html.EditorFor(m => m.Child3.GrandChild)
@Html.EditorFor(m => m.Child3.GrandChild2)

or if you have a model that is not fully known at design time. This might be the case when you have a polymorphic view that serves multiple types derived from a base class, or when you have a model created dynamically and hiding behind the metadata discovery mechanism.

The Solution

The method CollectDescendantProperties() traverses the model hierarchy and provides a 'flattened' list of properties complete with their metadata and full property path (eg 'Child3.GrandChild2'), ready to be used with @Html.Editor(desecendant.Path).

HTML
@functions
{
    IList<DescendantPropertyMetadata> CollectDescendantProperties(ModelMetadata modelMetadata)
    {
        var list = new List<DescendantPropertyMetadata>();
        CollectDescendantPropertiesRecursive("", list, modelMetadata, 0);
        return list;
    }

    void CollectDescendantPropertiesRecursive(string currentPath, 
    IList<DescendantPropertyMetadata> list, ModelMetadata modelMetadata, int incomingDepth)
    {
        int currentDepth = incomingDepth + 1;
        var complexProperties = ((IEnumerable<ModelMetadata>)modelMetadata.Properties.Where
                                (pr => pr.IsComplexType));
        foreach (var complexProperty in complexProperties)
        {
            string path = currentPath + (currentDepth == 1 ? "" : ".") + complexProperty.PropertyName;
            list.Add(new DescendantPropertyMetadata(path, currentDepth, complexProperty));
            CollectDescendantPropertiesRecursive(path, list, complexProperty, currentDepth);
        }
    }

    class DescendantPropertyMetadata
    {
        public DescendantPropertyMetadata(string path, int depth, ModelMetadata metadata)
        {
            Path = path;
            Depth = depth;
            Metadata = metadata;
        }
        public string Path { get; private set; }
        public int Depth { get; private set; }
        public ModelMetadata Metadata { get; private set; }
    }
}

You can use it in a view like this:

HTML
@Html.EditorForModel();
var desecendants = CollectDescendantProperties(ViewData.ModelMetadata);
foreach (DescendantPropertyMetadata desecendant in desecendants)
{
    <h2>@desecendant.Metadata.PropertyName</h2>
    @Html.Editor(desecendant.Path)
}

Note you would probably want to refractor the method code to somewhere outside a specific view.

History

  • Feb 2016 - Initial release

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Chief Technology Officer Ziv systems, Israel
Israel Israel
Starting with Apple IIe BASICA, and working my way through Pascal, Power Builder, Visual basic (and the light office VBA) C, C++, I am now a full stack developer and development manager. Mostly with MS technologies on the server side and javascript(typescript) frameworks on the client side.

Comments and Discussions

 
QuestionViewModels Pin
maynard_jk22-Feb-16 9:58
professionalmaynard_jk22-Feb-16 9:58 
AnswerRe: ViewModels Pin
Asher Barak23-Feb-16 0:34
professionalAsher Barak23-Feb-16 0:34 
Questionmaster detail Pin
Laurentiu LAZAR16-Feb-16 23:58
Laurentiu LAZAR16-Feb-16 23:58 
AnswerRe: master detail Pin
Asher Barak17-Feb-16 20:05
professionalAsher Barak17-Feb-16 20:05 
PraiseThis is exactly inline with my idea Pin
Vijay Bhasker Reddy CH16-Feb-16 22:29
Vijay Bhasker Reddy CH16-Feb-16 22:29 
GeneralRe: This is exactly inline with my idea Pin
Asher Barak17-Feb-16 20:24
professionalAsher Barak17-Feb-16 20:24 
QuestionTo whomever ranked this tip 1 Pin
Asher Barak16-Feb-16 1:10
professionalAsher Barak16-Feb-16 1:10 
QuestionGive source code and project solution in zip file Pin
Tridip Bhattacharjee15-Feb-16 20:53
professionalTridip Bhattacharjee15-Feb-16 20:53 
AnswerRe: Give source code and project solution in zip file Pin
Asher Barak16-Feb-16 1:02
professionalAsher Barak16-Feb-16 1:02 
Dear tbhattacharjee,

Thanks for pointing out the lack of code. I have uploaded a solution demonstrating the code. I hope the new version would be approved soon. Smile | :)

Best,

Asher
GeneralRe: Give source code and project solution in zip file Pin
Pritesh Bonde17-Feb-16 23:57
professionalPritesh Bonde17-Feb-16 23:57 
GeneralRe: Give source code and project solution in zip file Pin
Asher Barak18-Feb-16 0:44
professionalAsher Barak18-Feb-16 0:44 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.