1
votes

My problem is very similar to this Model binding issues with Kendo objects with complex child properties . The only difference is that i have another level in the object.

My model is:

Public Person 
{
   public int Id {get;set;}
   public string Name {get;set;}
   public IEnumerable<Course> Courses {get;set;}
}

public Course 
{
   public int Id {get;set;}
   public string Description {get;set;}
   public IEnumerable<Schedule> Schedules {get;set;}
}

Public Schedule 
{
   public DateTime Init {get;set;}
   public DateTime End {get;set;} 
}

This model is bound to a KendoGrid. Everything works well, except that Init and End properties are always null when I posted the model.

In the Ajax Datasource :

.Update(update => update.Action("Update", "Controller").Data("serialize"))
.Create(create => create.Action("Create", "Controller").Data("serialize"))

   <script>
        function serialize(data) {
           for (var property in data) {
            if ($.isArray(data[property])) {
                serializeArray(property, data[property], data);
            }
        }
    };

    function serializeArray(prefix, array, result) {
    for (var i = 0; i < array.length; i++) {
        if ($.isPlainObject(array[i])) {
            for (var property in array[i]) {
                result[prefix + "[" + i + "]." + property] = array[i][property];
            }
        }
        else {
            result[prefix + "[" + i + "]"] = array[i];
        }
    }
}
    </script>

What I have to do to send the properties of the lists schedules?

1

1 Answers

1
votes

I had also looked at their serializeArray solution, but it didn't work for me in case of 3 level objects I had. I could have fixed that but then I didn't want to write recursive code. The solution I used is pretty straight-forward and aligned to the problem I had. Its very readable.

I absolutely wish Kendo should do this out of the box for their grid, but they told this when I raised a support question.

"You will need to send the values as additional data in this case because the built-in filtering does not support collection values. To format the data so that it will be bound by the model binder, you should follow the guidelines from my previous reply(dot notation for objects and indexer for arrays)"

Here is my C# ViewModels

   //relates to one control value (for e.g. one entry in multi-select)
    public class FormUnitFilter
    {
        public string Operator { get; set; }
        public string Field { get; set; }
        public string Value { get; set; }
        public List<string> ValueList { get; set; }
    }

    //relates to a set of filters in a combined set (for e.g. the whole multi-select or a radiobutton or date control which appears in a single panel)
    public class FormSetFilter
    {
        public List<FormUnitFilter> Filters { get; set; }
        public string LogicalOperator { get; set; }
    }

    //relates to the whole set of filters present on the screen (for e.g. the filters across different panels)
    public class FormWholeFilter
    {
        public List<FormSetFilter> Filters { get; set; }
        public string LogicalOperator { get; set; }
    }

here is my js function which converts this json model to a type recognized by MVC controller action parameter.

function buildFilterCriteria() {
    var data = {};
    if (modelObj) {
        //reset the filters
        modelObj.FormWholeFilter.Filters.length = 0;

        //Assign FormWholeFilter data (outermost object)
        data["FormWholeFilter.LogicalOperator"] = modelObj.FormWholeFilter.LogicalOperator;
        //now iterate the filters inside FormWholeFilter (1st inner object)
        for (var setIndex = 0; setIndex < modelObj.FormWholeFilter.Filters.length; setIndex++) {
            var setFilter = modelObj.FormWholeFilter.Filters[setIndex];

            data["FormWholeFilter.Filters[" + setIndex + "].LogicalOperator"] = setFilter.LogicalOperator;

            //now iterate the filters inside FormSetFilter (2nd inner object)
            for (var unitIndex = 0; unitIndex < setFilter.Filters.length; unitIndex++) {
                var unitFilter = setFilter.Filters[unitIndex];

                data["FormWholeFilter.Filters[" + setIndex + "].Filters[" + unitIndex + "].Operator"] = unitFilter.Operator;
                data["FormWholeFilter.Filters[" + setIndex + "].Filters[" + unitIndex + "].Field"] = unitFilter.Field;
                data["FormWholeFilter.Filters[" + setIndex + "].Filters[" + unitIndex + "].Value"] = unitFilter.Value;

                if (unitFilter.ValueList)
                    for (var valIndex = 0; valIndex < unitFilter.ValueList.length; valIndex++) {
                        data["FormWholeFilter.Filters[" + setIndex + "].Filters[" + unitIndex + "].ValueList[" + valIndex + "]"] = unitFilter.ValueList[valIndex];
                    }
            }
        }
    }

    return modelObj && data;
}

Here is my controller action method which takes the Kendo grid datasourcerequest and the FormWholeFilter I pass from JavaScript.

    public JsonResult ProcessFilters([DataSourceRequest] DataSourceRequest request, FormWholeFilter formWholeFilter)
            {
//Method body
}

Also, when I load the page for the first time, I had assigned the modelObj to the FormWholeFilter blank json like this and thats why I could use this variable in the buildFilterCriteria method:

var modelObj;
$(document).ready(function () {
    modelObj = $.parseJSON('@Html.Raw(Json.Encode(@Model))');
});