Binding to asp.net mvc backend controller

17 posts, 1 answers
  1. Martin Moesby
    Martin Moesby avatar
    10 posts
    Member since:
    Feb 2014

    Posted 03 Jul Link to this post

    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...

     

     

     

  2. Tsvetina
    Admin
    Tsvetina avatar
    2087 posts

    Posted 05 Jul Link to this post

    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.
  3. Martin Moesby
    Martin Moesby avatar
    10 posts
    Member since:
    Feb 2014

    Posted 21 Jul in reply to Tsvetina Link to this post

    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...

     

     

  4. Tsvetina
    Admin
    Tsvetina avatar
    2087 posts

    Posted 21 Jul Link to this post

    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.
  5. Martin Moesby
    Martin Moesby avatar
    10 posts
    Member since:
    Feb 2014

    Posted 24 Jul in reply to Tsvetina Link to this post

    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

     

     
  6. Martin Moesby
    Martin Moesby avatar
    10 posts
    Member since:
    Feb 2014

    Posted 24 Jul Link to this post

    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?

  7. Martin Moesby
    Martin Moesby avatar
    10 posts
    Member since:
    Feb 2014

    Posted 24 Jul Link to this post

    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?

     

  8. Vasil
    Admin
    Vasil avatar
    1604 posts

    Posted 25 Jul Link to this post

    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.
  9. Martin Moesby
    Martin Moesby avatar
    10 posts
    Member since:
    Feb 2014

    Posted 26 Jul in reply to Vasil Link to this post

    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...

     

     

     

  10. Vasil
    Admin
    Vasil avatar
    1604 posts

    Posted 26 Jul Link to this post

    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.
  11. Martin Moesby
    Martin Moesby avatar
    10 posts
    Member since:
    Feb 2014

    Posted 26 Jul Link to this post

    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..:(

     

  12. Tsvetina
    Admin
    Tsvetina avatar
    2087 posts

    Posted 28 Jul Link to this post

    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.
  13. Martin Moesby
    Martin Moesby avatar
    10 posts
    Member since:
    Feb 2014

    Posted 31 Jul in reply to Tsvetina Link to this post

    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?

  14. Tsvetina
    Admin
    Tsvetina avatar
    2087 posts

    Posted 31 Jul Link to this post

    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.
  15. Martin Moesby
    Martin Moesby avatar
    10 posts
    Member since:
    Feb 2014

    Posted 31 Jul Link to this post

    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

     

     

  16. Answer
    Tsvetina
    Admin
    Tsvetina avatar
    2087 posts

    Posted 31 Jul Link to this post

    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.
  17. Martin Moesby
    Martin Moesby avatar
    10 posts
    Member since:
    Feb 2014

    Posted 31 Jul in reply to Tsvetina Link to this post

    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.

     

Back to Top