tags:

views:

150

answers:

5

In the context of C# 4, ASP.NET MVC 2, and NHibernate; I've got the following scenario:

Let's assume an entity Product that have an association to ProductType.

In a product edit view; how do I implement that only a sub-set of the product's properties are shown based on the ProductType association in an elegant and DRY way? I.e., different properties shall be shown for different values of a property of the ProductType.

Use a product view model builder, and from different view models automagically generate the view with my own Html.EditorForModel() (including drop-downs and other stuff not out-of-the-box)?

Attribute the properties of one view model and use the Html.EditorForModel() way aforementioned?

Use one model, but implement different web controls (view strategies) (can it be done DRY?)?

Something else entirely?

A: 

You can use EditorForModel and have only a subset of the properties get displayed if you add the attribute:

[ScaffoldColumn(false)]

To the property you don't want to display. Of course, if you implement your own custom editor template, then you have full control over what HTML gets rendered.

Tejs
How do you mean I should set the ScaffoldColumn to true/false based on a property value?
Martin R-L
Sorry if I was not clear - you apply this attribute to each property that you don't want to be displayed with the default EditorFor helper. Setting it to false will not display that property. You can then customize the output with the Data Annotations.
Tejs
I got that. My question (still) is: how to render some properties in an edit view where the rendered properties differ based on a property of the (view) model?ScaffoldColumn is a static solution for one case. Right? The way attributes are implemented, they don't allow for function calls as parameters (must be a constant expression).Otherwise, it'd be lovely to do something like [ScaffoldColumn(m => ShallShowSpec.IsSatisfiedBy(m))].If you still think you've solved the problem; please provide working code, or enlighten me some other way.
Martin R-L
I've (hopefully) clarified my question by editing the original question. Sorry if I misguided you!
Martin R-L
+1  A: 

Ah I see - my apologies. That's not 'technically' supported - however, you could alter a custom attribute to use some funky reflection to achieve the same thing. That's definitely not a best practice though.

Another option would be to use Html.EditorFor(m => m.ProduceView()), where ProduceView is a method that returns a new ViewModel type based on the state of your properties - so if some Property is set, then ProduceView sends a SetPropertyViewModel, or a NotSetPropertyViewModel, both of which who implement some base class or interface. Each of those view models could be then annotated differently.

Tejs
+1 for your insights.
Martin R-L
A: 

create a custom DataAnnotationAttribute that takes in a ProductType as a parameter. Then apply to the corresponding properties that are to be viewed. You can the extend this functionality further by creating a EditorTemplate for Product that will handle further process, form element look and feel, or and some JQuery to some form elements.

JaySharp13
+1  A: 

My first step is to create a view model. Even if this is very similar to your actual entity, the separation is important. so I would create a ProductEditViewModel class.

Next, determine the properties that will be changing based on the different product types. Create separate Partial View Models for each product type. This allows you control over what properties are displayed and how they are formatted.

In your main Product Edit view, use a switch statement to "swap in and out" the different partial views, as required. If you use AJAX, you can even do this dynamically.

In this example, we have a number of different reports that have different report types. The main part of the reports doesn't change, just a number of different parameters (depending on type).

For each report type we have separate partial views, which you can see are added in depending on the report type. This code snippet is inside a <% using (Html.BeginForm()) %> code block.

          <% switch (Model.ReportType)
          {
              case (int)ReportType.summary:
                  Html.RenderPartial("Edit/SummaryControl", Model);
                  break;
              case (int)ReportType.exception:
                  Html.RenderPartial("Edit/ExceptionControl", Model);
                  break;
              case (int)ReportType.leakdetection:
                  Html.RenderPartial("Edit/LeakDetectionControl", Model);
                  break;
          } %>

and the summary report partial view:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Footprint.Web.ViewModels.ReportsEditViewModel>" %>
<fieldset>
    <legend>Summary Report Parameters</legend>
    <div class="editor-label">
        <%= Html.LabelFor(model => model.Frequency)%>
    </div>
    <div class="editor-field">
        <%= Html.DropDownListFor( model => model.Frequency,Model.Frequencies) %>
        <%= Html.ValidationMessageFor(model => model.Frequency)%>
    </div>
    <div class="editor-label">
    </div>
    <div class="editor-field">
        <%= Html.CheckBoxFor(model => model.Combine) %><%= Html.LabelFor(model => model.Combine)%>
    </div>
</fieldset>

HTH

Alastair Pitts
+1 for your insights.
Martin R-L
A: 
Martin R-L