0

I'm building a CMS where I can dynamically say what types of editors will be shown. For instance on 1 type of page I might want a textbox and a textarea, and another a textbox and datepicker

I've defined the fields

public class CMSField
{
    public string Label { get; set; }
}

public class CMSTextField : CMSField
{
    public string Value { get; set; } 
}

public class CMSDatePickerField : CMSField
{
    public DatePickerControl Value { get; set; }
}

My view model

public class CmsVM
{
    public List<CMSField> fields { get; set; }

    public CmsVM()
    {
        fields= new List<CMSField>();
    }
}

Then in the controller I can set this up like so (manually defined for now but will be defined elsewhere):

    public ActionResult Add()
    {
        CmsVM model = new CmsVM();
        model.fields.Add(new CMSTextField() { Label = "Title", Value = "" });
        model.fields.Add(new CMSDatePickerField()
        {
            Label = "Date",
            Value = new DatePickerControl
                {
                    SelectedDate = DateTime.Now
                }
        });

        return View("Add", "_FormLayout", model);
    }

Then in my view I show the relevant editor e.g.

@model CmsVM
@for (var i = 0; i < Model.fields.Count(); i++)
{
    var cmsField = Model.fields[i];
    if (cmsField is CMSTextField)
    {
        var textBox = (CMSTextField)cmsField;

        @Html.TextBoxFor(model => textBox.Value)
    }
    else if (cmsField is CMSDatePickerField)
    {
        var dp = (CMSDatePickerField)cmsField;
        @Html.EditorFor(model => dp.Value)
        @Html.ValidationMessageFor(model => dp.Value)
    }
}

All good so far, I get whatever controls I define in the controller shown. But I now need to deal with the postback. The model is always null

    [HttpPost]
    public ActionResult AddNew(CmsVM model)
    {
    ...
    }

Is there a better way to set this up, so that I can post the viewmodel back?

user2436996
  • 181
  • 2
  • 9
  • Look at the html your generating - the `name` attributes have no relationship to your model properties. It needs to be `@Html.TextBoxFor(m => m.fields .Value)` to match your model –  Sep 27 '17 at 09:41
  • But of course that will not work since your `CMSField` base class does not have a property named `Value`, and it would never bind when you POST back anyway since the `DefaultModelBinder` has no way of knowing which concrete class you want. –  Sep 27 '17 at 09:44
  • Seems that you don't understand how model class inheritance works (see this similar issue: https://stackoverflow.com/questions/5460081/asp-net-mvc-3-defaultmodelbinder-with-inheritance-polymorphism). Try using custom model binder derived from `DefaultModelBinder`. – Tetsuya Yamamoto Sep 27 '17 at 09:54
  • Thanks @Tetsuya, that's interesting and makes sense if I had a base CMSPage type then inherited page types with different definitions. My case is a little different as the ViewModel is always the same class "CmsVM" but it's fields wil be different. A custom model binder may still be the answer though. – user2436996 Sep 27 '17 at 10:22

0 Answers0