This is a migrated thread and some comments may be shown as answers.

Strange Forms values behavior with complex objects

3 Answers 294 Views
DropDownList
This is a migrated thread and some comments may be shown as answers.
Mo
Top achievements
Rank 1
Mo asked on 23 Jul 2012, 10:11 PM
I am using a Kendo UI DropDownList to allow users to choose a State on 2 different pages. On page A, the StateId property being edited is on the core model object. On page B, the StateId property is 1 object deeper. The 2 pages are for editing TaxRules and Customers, with the Model classes as follows:

public class TaxRuleModel {
public int StateId {get; set;}
}
public class CustomerModel {
public AddressModel Address {get; set;}
}
public class AddressModel {
public int StateId {get; set;}
}

Both pages create a drop down list as follows, with the selector being the only difference:  
input[name='StateId'] vs input[name='Address.StateId']

 $(selector).kendoDropDownList({
        dataTextField: "DisplayText",
        dataValueField: "Value",
        optionLabel: "Select One",
        dataSource: {
        type: "json",
        error: handleKendoGridCrudError,
        transport: { read: {Url: url, dataType: "json"}},
        schema: { 
            model:
            {
                fields:
                    {
                        Value: { type: "number" },
                        DisplayText: { type: "string" }
                    }
            }
        }
        }
    });

The URL returns a JSON list of :

public class StateModel
{
public int Value {get; set;}
public String DisplayText {get; set;}
}

The MVC methods to handle saving the models on each page have the following signatures:

public ActionResult Edit([DataSourceRequest] DataSourceRequest dataSourceRequest, TaxRule model);
public ActionResult Edit([DataSourceRequest] DataSourceRequest dataSourceRequest, Customer model); 

The drop down list appears as expected on both pages. On page A, when editing the TaxRule objects, the Form key with the value of the StateId is "StateId" as expected. This allows the StateId value to automatically bind to the TaxRule model as expected and be available in the action method on the model instance. However, on page B, when editing the Customer object, the Form has 2 keys related to the StateId: "Address.StateId.Value" and "Address.StateId.DisplayText". Because of this, the StateId never binds to the Customer.Address.StateId property of the model in MVC. Clearly the problem is that Kendo is attaching the entire lookup list State object to Address.StateId. But I can't figure out why it would behave differently in these 2 different cases. My schema model for the TaxRule and Customer objects look like this:

model: // TaxRule
    {
        id: "Id",
        fields:
            {
                Id: { defaultValue: 0 },
                StateId: {}
            }
    }

model: // Customer
    {
        id: "Id",
        fields:
            {
                Id: { type: "number", defaultValue: 0 },
                Address:
                    {
                        defaultValue: {},
                        Id: { type: "number", defaultValue: 0 },
                        StateId: {}
                    }
            }
    }

How can I get this to work properly?

3 Answers, 1 is accepted

Sort by
0
Mo
Top achievements
Rank 1
answered on 24 Jul 2012, 07:05 PM
I finally figured this out- it is a JSON object initialization issue. I noticed that editing works fine- it is only adding that was creating the problem. The solution is, for the complex object, to initialize the StateId property. However, this HAS to be done in the Address's defaultValue JSON. Just specifying a defaultValue for the StateId will not work (I still need to research why).


model: // Customer
    {
        id: "Id",
        fields:
            {
                Id: { type: "number", defaultValue: 0 },
                Address:
                    {
                        defaultValue: {
StateId: 0
},
                        Id: { type: "number", defaultValue: 0 },
                        StateId: {}
                    }
            }
    }
0
Nathan
Top achievements
Rank 1
answered on 23 Oct 2012, 09:00 AM
I want to sincerely thank you, you were my inspiration for my workaround.
I was lost in 2 problems using the MVC wrappers:

1. It is impossible to set a default value for a complex type (Entity) using the wrappers,
the result would have been an exception:
"could not serialize object 'ObjectType'"

Kendo claims they will fix this matter soon, something of that sort:
http://www.kendoui.com/forums/mvc/grid/cannot-set-default-value-of-a-guid-column.aspx
This example is with GUID but same result with your own entites and other data components (listview etc..)

2. I didn't want to go all Javascript and lose the advantage of using the Wrappers,
I have a complex editing template and it would have been a nightmare to go back to primitive javascript.

Looking into the Wrappers' generated code, it looked the following:
model: ComplexObject: {type:"object"}

I needed it to be
model: ComplexObject:
{ defaultValue: {Id: 0}, Id: { type: "number", defaultValue: 0 }, Name: { type: "string", defaultValue: "Default" }}

So I've implemented an ActionFilter that changes the HTML result right before it's sent to the client:

    public class ComplexObjectListviewFix : ActionFilterAttribute
    {
        ....
        public override void OnResultExecuting(ResultExecutingContext filterContext)
        {
            var request = filterContext.HttpContext.Request;
            var response = filterContext.HttpContext.Response;

            // Change the
            response.Filter = new PreResultFilter(response.Filter, s =>
                    {
                        s = s.Replace("ComplexObject:{type:\"object\"}", "ComplexObject:" +
                        "{ defaultValue: {Id: 0}, Id: { type: \"number\", defaultValue: 0 }, Name: { type: \"string\", defaultValue: \"Default\" }}");
                        return s;
                    });

        }
        ....

    }

This allowed me just to mark as an attribute the controllers that use that listview:
[ComplexObjectListviewFix ]
public ActionResult CompleObjects(int id = 0)

Worked like a charm, I'd be willing to share some more code if needed,
you are my hero for today ;)
Thank YOU!
0
Mo
Top achievements
Rank 1
answered on 23 Oct 2012, 05:47 PM
Nice, I like the approach. I am not using the wrappers- I abandoned them a week or so after starting because they limited too many things. I hadn't thought to approach the problems in that manner with the ActionFilters. Since you have complex objects, you may want to be aware of another issue I came across. I submitted as a bug, but they informed me that the handling I am looking for is not part of KendoUI, so I had to stick with my workaround. Anyway, for what it's worth, bug is 607251. The issue is:

I am using the DataSource object in a grid. The DataSource retrieves the data from a webservice which returns data in JSON notation. Date type properties in the immediate data objects have their JSON string date values converted into Date objects. But Date type properties nested in subobjects are left as string, even when the model defines them as dates. An example model that would have this problem would be:

fields:
                {
                    Id: { type: "number", defaultValue: 0 },
                    Visitor: {
                        Id: {},
                        LastActivityDate: { type: "date" }
                    CreatedDate: { type: "date" }
                }

In the code above, CreatedDate would be correctly converted to a Date object, but Visitor.LastActivityDate is left as a string. This prevents formatting in the grid. I was able to workaround this by converting the properties myself in the DataSourceChanged event below. 

if (e.items !== null) {
                for (i in e.items) {
                    if (e.items[i].Visitor != null) {
                        e.items[i].Visitor.LastActivityDate = new Date(e.items[i].Visitor.LastActivityDate.match(/\d+/)[0] * 1);
                    }
                }
            }
Tags
DropDownList
Asked by
Mo
Top achievements
Rank 1
Answers by
Mo
Top achievements
Rank 1
Nathan
Top achievements
Rank 1
Share this question
or