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

Binding to asp.net mvc backend controller

16 Answers 1400 Views
Data Source
This is a migrated thread and some comments may be shown as answers.
Martin Moesby
Top achievements
Rank 2
Martin Moesby asked on 03 Jul 2017, 08:11 AM

I have a problem with the datasource, when binding it to an asp.net MVC backend controlller for READ and UPDATE.

The READ function works when using the parameterMap like :

if (operation == "read") {
return options;
};

The UPDATE function, however, does NOT:

if (operation != "read" && options.models) {
return { models: kendo.stringify(options.models) };
};

When saving the grid back to the backend controller, an error occurs in kendo.all.js.

This i my Datagrid - sourcecode:

var gridDatasource = new kendo.data.DataSource({
    transport: {
        read: {
            url: "@Url.Action("GetBudgetData","Dagsbudget")",
            dataType: "json",
            type: "POST",
            data:
            {
                butik: $("#location").data("kendoDropDownList").value(),
                budgetyear: periodeSelector.getFullYear(),
                budgetmonth: periodeSelector.getMonth() + 1
            }
        },
        update: {
            url: "@Url.Action("PutBudgetData","Dagsbudget")",
            type: "POST",
            dataType: "json",
        },
        parameterMap: function (options, operation) {
 
            if (operation != "read" && options.models) {
                return { models: kendo.stringify(options.models) };
            };
            if (operation == "read") {
                return options;
            };
 
        },
        success: function (e) {
            alert("Get budget data success");
        },
        error: function (xhr, status, error) {
            debugger;
            alert(error);
        },
        schema: {
            model: {
                fields: {
                    location: { type: "text", ediable: false },
                    date: { type: "date", editable: false, format: "dddd, 'd.' dd-MM-yyyy" },
                    lastYear: { type: "number", editable: false },
                    budget: { type: "number", editable: true },
                    fontcolor: { type: "text", editable: false }
                }
            }
        },
        batch: true
        }
    });

My Grid definition:

$("#budgetGrid").kendoGrid({
    dataSource: gridDatasource,
    toolbar: ["save","cancel"],
    pageable: false,
    editable: "inline",
    navigatable: true,
    groupable: false,
    sortable: false,
    pageable: false,
    columns: [
        {
            field: "location",
            hidden: true
 
        },
        {
            field: "date",
            title: "Day",
            width: 180,
            template: "<span style='color:#=fontColor#'>#= kendo.toString(date, 'dddd, dd-MM-yyyy') #</span>"
        },
        {
            field: "lastYear",
            title: "Sidste Ã¥rs oms.",
            width: 80,
            format: "{0:c0}",
            attributes: { style: "text-align:right;" }
 
        },
        {
            field: "budget",
            title: "Budget",
            width: 80,
            format: "{0:c0}",
            attributes: { style: "text-align:right;", class: "budgetamount" }
        }
    ]
});

My backend controller actions:

[HttpPost]
public JsonResult GetBudgetData(string butik, int budgetyear, int budgetmonth)
{
    return Json(db.GetDailyBudget(butik, new DateTime(budgetyear, budgetmonth, 1)));
}
 
[HttpPut]
public JsonResult PutBudgetData(IEnumerable<DailyBudget> budgets)
{
    foreach (DailyBudget item in budgets)
    {
        // Do something....
    }
    return Json(null);
}

 

Either the update-routine of the grid doesn't fire or an error occurs in the kendo-all.js file...

I have tried all the tricks, tips and workarounds I can find or think of, but nothing seems to be working...

 

 

 

16 Answers, 1 is accepted

Sort by
0
Tsvetina
Telerik team
answered on 05 Jul 2017, 07:52 AM
Hi Martin,

One thing that I notice in your code is that you declare the controller method for update with HttpPut verb, but in the Grid, you have configured the DataSource to make a POST request for updates. Try changing this to PUT. 

Additionally, can you tell what is the error in the DataSource when the update method is  hit? This could point us in the direction of the problem if the non-matching verbs are not the only issue.

Regards,
Tsvetina
Progress Telerik
Try our brand new, jQuery-free Angular 2 components built from ground-up which deliver the business app essential building blocks - a grid component, data visualization (charts) and form elements.
0
Martin Moesby
Top achievements
Rank 2
answered on 21 Jul 2017, 07:53 AM

Hi,

Thank you for the reply, but alas, nothing changed...

I have tried to remove the HttpPut predicate of my backend controller action...

But it seems that the grid does not invoke the update-function of the datasource anymore? Now I don't even get the error from the kendo-all-js anymore.

The readonly-oprion of the fields in the model-definition are also ignored by the grid...

 

 

0
Tsvetina
Telerik team
answered on 21 Jul 2017, 11:33 AM
Hello Martin,

Could you paste the error that you are getting when the Update method is hit? This should give us a better idea of the problem. Additionally, have you checked the parameterMap function to confirm that it produces well structured data? 

One problem that I now notice with the Grid is that it uses "inline" editing, while the DataSource (and action method) is set to support batch editing. Note that batch editing works only with "incell" editing mode (demo).
This being said, you should either change the Grid editable property to "incell" or you should disable batch mode for the DataSource. If you disable batch editing, your action method should accept a single model:
[HttpPut]
public JsonResult PutBudgetData(DailyBudget budget)
{
    // save budget
    return Json(null);
}

If this does not help, it would be best if you can prepare a runnable sample and send it to us for further debugging.


Regards,
Tsvetina
Progress Telerik
Try our brand new, jQuery-free Angular 2 components built from ground-up which deliver the business app essential building blocks - a grid component, data visualization (charts) and form elements.
0
Martin Moesby
Top achievements
Rank 2
answered on 24 Jul 2017, 06:31 AM

Hi Tsvetina,

My problem is now a different problem...

The UPDATE function of the dataSource does not fire at all and neither the 'editable' or the 'format' property does not seem to get applied in the grid

My datasource definition is this:

var gridDatasource = new kendo.data.DataSource({
    batch: true,
    transport: {
        read: {
            url: "@Url.Action("GetBudgetData","Dagsbudget")",
            dataType: "json",
            type: "GET",
            data:
            {
                butik: $("#location").data("kendoDropDownList").value(),
                budgetyear: new Date($("#yearmonthpicker").data("kendoDatePicker").value()).getFullYear(),
                budgetmonth: new Date($("#yearmonthpicker").data("kendoDatePicker").value()).getMonth() + 1
            }
        },
        update: {
            url: "@Url.Action("PutBudgetData","Dagsbudget")",
            type: "POST",
            contentType: "application/json",
            dataType: "json"
        },
        parameterMap: function (options, operation) {
 
            if (operation != "read" && options.models) {
                return { models: kendo.stringify(options.models) };
            };
 
            if (operation == "read") {
                return options;
            };
        },
        schema: {
            model: {
                id: "Id",
                fields: {
                    Id: { type: "text", editable: false },
                    Location: { type: "text", editable: false },
                    Date: { type: "date", editable: false, format: "dddd, 'd.' dd-MM-yyyy" },
                    LastYear: { type: "number", editable: false, format: "{0:c}" },
                    Budget: { type: "number", editable: true, format: "{0:c}" },
                    FontColor: { type: "text", editable: false }
                }
            }
        }
    }
});

My grid definition is like this:

$("#budgetGrid").kendoGrid({
       dataSource: gridDatasource,
       toolbar: ["save", "cancel"],
       pageable: false,
       editable: true,
       navigatable: true,
       groupable: false,
       sortable: false,
       pageable: false,
       columns: [
           {
               field: "id",
               hidden: false,
               width: 100,
               readonly: true
           },
           {
               field: "location",
               hidden: true
           },
           {
               field: "Date",
               title: "Dato",
               width: 180,
               template: "<span style='color:#=fontColor#'>#= kendo.toString(kendo.parseDate(date, 'yyyy-MM-dd'), 'dddd, dd-MM-yyyy') #</span>"
           },
           {
               field: "lastYear",
               title: "Sidste Ã¥rs oms.",
               width: 80,
               format: "{0:c0}",
               attributes: { style: "text-align:right;" }
           },
           {
               field: "budget",
               title: "Budgetbeløb",
               width: 80,
               format: "{0:c0}",
               attributes: { style: "text-align:right;", class: "budgetamount" }
           }
       ]
   });

and my back-end controller action is this:

[HttpPost]
public ActionResult PutBudgetData(IEnumerable<DailyBudget> gridData)
{
    string result = string.empty();

    // Do something

    return Json(null);
}

I have tried to use a different function outside of the grid/datasource:

<button id="saveButton">Save Data</button>
 
<script>
 
    $("#saveButton").kendoButton({
        icon: "refresh",
        click: function () {
 
            var budgetData = $("#budgetGrid").data().kendoGrid.dataSource.view();
 
            $.ajax({
                url: "@Url.Action("PutBudgetData","Dagsbudget")",
                type: "POST",
                contentTYpe: "application/json",
                type:"POST",
                data: {
                    gridData: JSON.stringify(budgetData);
                },
                success: function (result) {
                    alert("Data Saved");
                },
                error: function (xhr, status, error) {
                    alert(error);
                }
 
            });
        }
 
    });
 
</script>

 

This causes the backend-action to get hit, but the gridData-parameter is empty?

this seems to indocate to me, that there's a typo somewhere, but I simply cannot see it?

Any help is appreciated

 

 
0
Martin Moesby
Top achievements
Rank 2
answered on 24 Jul 2017, 08:14 AM

OK, I found my typo:

I accidentally set the schema as part of the transporter.

After moving the schema out to the root of the grid-definition, I could get the update-function to fire and then get the error message from kendo.all.js.

the error is :

Unhandled exception at line 27, column 19104 in http://localhost:9485/js/kendo.all.min.js
0x800a138f - Der opstod en JavaScript-kørselsfejl: Egenskaben 'data' kan ikke hentes for reference, der er udefineret eller null occurred

(Roughly translated : "The property ' data' cannot be retrieved for a reference that is undefined or null")

The function in which this is occurring is this one:

01.var RemoteTransport = Class.extend({
02.    init: function (options) {
03.        var that = this, parameterMap;
04.        options = that.options = extend({}, that.options, options);
05.        each(crud, function (index, type) {
06.            if (typeof options[type] === STRING) {
07.                options[type] = { url: options[type] };
08.            }
09.        });
10.        that.cache = options.cache ? Cache.create(options.cache) : {
11.            find: noop,
12.            add: noop
13.        };
14.        parameterMap = options.parameterMap;
15.        if (isFunction(options.push)) {
16.            that.push = options.push;
17.        }
18.        if (!that.push) {
19.            that.push = identity;
20.        }
21.        that.parameterMap = isFunction(parameterMap) ? parameterMap : function (options) {
22.            var result = {};
23.            each(options, function (option, value) {
24.                if (option in parameterMap) {
25.                    option = parameterMap[option];
26.                    if (isPlainObject(option)) {
27.                        value = option.value(value);
28.                        option = option.key;
29.                    }
30.                }
31.                result[option] = value;
32.            });
33.            return result;
34.        };
35.    },
36.    options: { parameterMap: identity },
37.    create: function (options) {
38.        return ajax(this.setup(options, CREATE));
39.    },
40.    read: function (options) {
41.        var that = this, success, error, result, cache = that.cache;
42.        options = that.setup(options, READ);
43.        success = options.success || noop;
44.        error = options.error || noop;
45.        result = cache.find(options.data);
46.        if (result !== undefined) {
47.            success(result);
48.        } else {
49.            options.success = function (result) {
50.                cache.add(options.data, result);
51.                success(result);
52.            };
53.            $.ajax(options);
54.        }
55.    },
56.    update: function (options) {
57.        return ajax(this.setup(options, UPDATE));
58.    },
59.    destroy: function (options) {
60.        return ajax(this.setup(options, DESTROY));
61.    },
62.    setup: function (options, type) {
63.        options = options || {};
64.        var that = this, parameters, operation = that.options[type], data = isFunction(operation.data) ? operation.data(options.data) : operation.data;
65.        options = extend(true, {}, operation, options);
66.        parameters = extend(true, {}, data, options.data);
67.        options.data = that.parameterMap(parameters, type);
68.        if (isFunction(options.url)) {
69.            options.url = options.url(parameters);
70.        }
71.        return options;
72.    }
73.});

- line no. 64

Hopes this helps - i s it something with my ParameterMap setup in the transporter-definition?

0
Martin Moesby
Top achievements
Rank 2
answered on 24 Jul 2017, 11:23 AM

OK,

Now I have the update function working...apart from one tiny, but crucial thing:

The data from the grid does not reach the back-end controller action?

the dataSource now looks like this:

var gridDatasource = new kendo.data.DataSource({
    batch: true,
    transport: {
        read: {
            url: geturl,
            dataType: "json",
            contentType: "application/json",
            type: "GET",
            data:
            {
                butik: $("#location").data("kendoDropDownList").value(),
                budgetyear: new Date($("#yearmonthpicker").data("kendoDatePicker").value()).getFullYear(),
                budgetmonth: new Date($("#yearmonthpicker").data("kendoDatePicker").value()).getMonth() + 1
            }
        },
        update: {
            url: saveurl,
            type: "POST",
            //contentType: "application/json",
            //dataType: "json"
 
        },
        parameterMap: function (data, type) {
 
            if (type == "read") {
                return data;
            };
 
            if (type = ! "read" && data.models) {
                return kendo.stringify(data.models);
             };
        },
 
    },
    change: function (e) {
 
        recalculateOverview();
 
    },
    error: function (e, jqxhr) {
        console.log(e, jqxhr);
        alert(jqxhr.ResponseText);
 
    },
    schema: {
        model: {
            id: "id",
            fields: {
                id: { type: "text", editable: false },
                location: { type: "text", editable: false },
                date: { type: "date", editable: false, format: "dddd, 'd.' dd-MM-yyyy" },
                lastYear: { type: "number", editable: false, format: "c0" },
                budget: { type: "number", editable: true, format: "c0" },
                fontColor: { type: "text", editable: false }
            }
        }
    }
 
});

 

My Grid definition :

$("#budgetGrid").kendoGrid({
    dataSource: gridDatasource,
    toolbar: ["save", "cancel"],
    editable: true,
    navigatable: true,
    columns: [
        {
            field: "id",
            hidden: true,
        },
        {
            field: "location",
            hidden: true
        },
        {
            field: "date",
            title: "Dato",
            width: 180,
            template: "<span style='color:#=fontColor#'>#= kendo.toString(kendo.parseDate(date, 'yyyy-MM-dd'), 'dddd, dd-MM-yyyy') #</span>"
        },
        {
            field: "lastYear",
            title: "Sidste Ã¥rs oms.",
            width: 80,
            format: "{0:c0}",
            attributes: { style: "text-align:right;" }
        },
        {
            field: "budget",
            title: "Budgetbeløb",
            width: 80,
            format: "{0:c0}",
            attributes: { style: "text-align:right;", "class": "budgetamount"}
        }
    ],
    dataBound: function (e) {
        //recalculateOverview();
    }
});

 

and finally, my backend-action (unchanged):

[HttpPost]
public ActionResult PutBudgetData(IEnumerable<DailyBudget> gridData)
{
    foreach (var item in gridData)
    {
        //Do stuff with the data...
    }
    return Json(null);
}

 

Is there anybody out there who can tell me, where I've gone wrong?

 

0
Vasil
Telerik team
answered on 25 Jul 2017, 01:14 PM
Hi Martin,

The Action method is not fired, because the framework is not able to match the post data to the parameters for some reason. We could not be sure why, until you show the exact query that is sent to the server.

If you send us the query we will able to give you more specific information.

Meanwhile, you could try to specify the type in the parameters as List<DailyBudget> gridData, instead IEnumerable<DailyBudget> gridData

Additional option is to follow the Demo that Tsvetina refereed to. This approach is tested and proved to be working. Set the dataType to jsonp. Modify the parameterMap as follows:

parameterMap: function (data, type) {
    if (type == "read") {
        return data;
    }
 
    if (options.models) {
       return {models: kendo.stringify(options.models)};
    }
}

And here is the Action method used for the demo, it is defined without parameters, and then DeserializeObject method is used to access and deserialize the "models" property.
https://github.com/telerik/kendo-ui-demos-service/blob/master/KendoCRUDService/Controllers/ProductsController.cs#L21

Such approach also give you the ability to place breakpoint into the Action to debug it if something is not correct. The action should be fired even if the parameters are not correct, since it is defined without parameters.


Regards,
Vasil
Progress Telerik
Try our brand new, jQuery-free Angular 2 components built from ground-up which deliver the business app essential building blocks - a grid component, data visualization (charts) and form elements.
0
Martin Moesby
Top achievements
Rank 2
answered on 26 Jul 2017, 12:50 PM

Hi Vasil,

Thank you for your suggestions, but that did not Work.

However, the ContentLength to the backend action DID containt data, and the i hade to manually map the content from the POST into my data, so by addign this code to my controller-action...

Stream req = Request.Body;
string json = new StreamReader(req).ReadToEnd();
 
gridData = JsonConvert.DeserializeObject<List<DailyBudget>>(json);

 

.. I got data that I could manipulate.

So for now, ut's working, and my gues is that it's the MVC5 mapper, that's messing things up...

The backend-object has properties defined with a starting capital-letter, but the data received by grid-datasource id not with a starting capital-letter...

Anyways, the issue is resolved and I am happy :)

Thank you for your patience...

 

 

 

0
Vasil
Telerik team
answered on 26 Jul 2017, 04:06 PM
Hi Martin,

I am glad that you have resolved the issue.

I will place some more information here for your latest findings so it could help others as well.

When using the .NET Core, the capital-letter/cammelCase issue can be caused by the breaking change that Microsoft made in the default JSON serializer.

This can be configured in the startup.cs of the application:

public void ConfigureServices(IServiceCollection services)
{
    ...
    // Maintain property names during serialization. See:
    services
        .AddMvc()
        .AddJsonOptions(options => options.SerializerSettings.ContractResolver = new DefaultContractResolver());
 
    // Add Kendo UI services to the services container
    services.AddKendo();
}
Further information is also available here: http://docs.telerik.com/aspnet-core/getting-started/getting-started

Regards,
Vasil
Progress Telerik
Try our brand new, jQuery-free Angular 2 components built from ground-up which deliver the business app essential building blocks - a grid component, data visualization (charts) and form elements.
0
Martin Moesby
Top achievements
Rank 2
answered on 26 Jul 2017, 04:58 PM

Thank you, Vasil,

But after implementing that change, and fixing the JavaScript code back to capital-letter, I am now BACK at the original JavaScript error:

Unhandled exception at line 27, column 20138 in http://localhost:9486/js/kendo.all.min.js
0x800a138f - Der opstod en JavaScript-kørselsfejl: Egenskaben 'data' kan ikke hentes for reference, der er udefineret eller null occurred

 

translated roughly as "Property 'data' cannot be read for reference that is undefined or 'NULL' occurred...

This only occurs when invoking the update-function of the transporter to send updated data back to the Controller action, which it never reaches so I cannot check the Action itself.

The read function of the transporter works as a charm.

Dont know where to go from here..:(

 

0
Tsvetina
Telerik team
answered on 28 Jul 2017, 11:43 AM
Hello Martin,

Do you think that you can prepare a sample project, where we can reproduce the issue? This will make it possible for us to debug it and find the cause of the issue much faster. You can replace the actual data returned by the Read method with a few mocked items to be able to isolate the problem.

Regards,
Tsvetina
Progress Telerik
Try our brand new, jQuery-free Angular 2 components built from ground-up which deliver the business app essential building blocks - a grid component, data visualization (charts) and form elements.
0
Martin Moesby
Top achievements
Rank 2
answered on 31 Jul 2017, 07:55 AM

Hi Tsvetina,

I have prepared a VS2017 project that will run without access to the database (which is actually located in Greenland :)

But I cannot attach it because it's around 8MB and there's a limit of 2MB - even with the entire kendo-library removed...

Is there som other way, that I can share this project with you?

0
Tsvetina
Telerik team
answered on 31 Jul 2017, 08:04 AM
Hi Martin,

You can open a support ticket from your Telerik account, there the limit on the attachment is 20MB. Alternatively, if your project does not contain sensitive information, you could upload it to a file sharing service or a cloud storage provider of your choice and send us a link.

Regards,
Tsvetina
Progress Telerik
Try our brand new, jQuery-free Angular 2 components built from ground-up which deliver the business app essential building blocks - a grid component, data visualization (charts) and form elements.
0
Martin Moesby
Top achievements
Rank 2
answered on 31 Jul 2017, 08:16 AM

The ZIP-file can be accessed here: https://1drv.ms/u/s!AseHPSqmtv3Tgew1RvXwKKm4T27dYg

if you have questions, you can reach me at the phone-number on my profile...

Again - thank you for your effort and commitment to this, especially since I suspect this to be a configuration-error on my part

 

 

0
Accepted
Tsvetina
Telerik team
answered on 31 Jul 2017, 10:24 AM
Hi Martin,

In the project you sent me, the problem was caused by the casing of the "id" field setting in the DataSource schema:
schema: {
    model: {
        id: "id", // => should be Id

Another problem that could lead to more problems with the Grid editing is that all Id values came back as null from the server. I assume that you missed that only when preparing the sample data for the project and that your actual project has values for the items Id field. In order for CRUD operations to work without errors or glitches, it is required that there is an Id field declared which:
  • has non-null values for all data items
  • has unique value for each data item

Let me know if there are any issues remaining after you change the Id field casing in the schema definition.

Regards,
Tsvetina
Progress Telerik
Try our brand new, jQuery-free Angular 2 components built from ground-up which deliver the business app essential building blocks - a grid component, data visualization (charts) and form elements.
0
Martin Moesby
Top achievements
Rank 2
answered on 31 Jul 2017, 10:50 AM

Hi, Tsvetina,

In the regular datasource the ID does have a value, its just a missed property when I created the mock-up data function. 

Thank you - Like I said - an Transporter Configuration Error on my part :) (or as we say in Denmark - an Error 40)

I fixed it in the transporter, and now the Update-function fires as expected

The incoming object in the backend controller (griddata) is stil empty though, but like I wrote earlier I'll address this to be in an error in the deserialization of MVC5.

I think we should close the issue here, since you have given me ALL the answers I needed and more....

Thank you very much for your patience and commitment.

 

Tags
Data Source
Asked by
Martin Moesby
Top achievements
Rank 2
Answers by
Tsvetina
Telerik team
Martin Moesby
Top achievements
Rank 2
Vasil
Telerik team
Share this question
or