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

Kendo grid returning null values in my model on Web API end

6 Answers 2527 Views
Grid
This is a migrated thread and some comments may be shown as answers.
Michael
Top achievements
Rank 1
Michael asked on 07 Jun 2016, 03:03 PM

Hi, I'm having an issue with my Kendo Grid where when the update event hits my web api controller, the model is showing but all the values are null or 0. I've done this months back with another company and I got it working just fine, but this time around, it's not working.

Here's the relevant snippet on the controller end (and yes, it's hitting right correct controller).

1.[HttpPut]
2.[ResponseType(typeof(spLineItemsGetAllForOrder_Result))]
3.[Route("Orders/UpdateLineItems")]
4.public int UpdateLineItems([FromBody]spLineItemsGetAllForOrder_Result models)
5.{

Even without line 2, the responseType, still doesn't change anything. A long time ago, when we were first having this problem with another company, the key factor was to place the [FromBody] attribute in there. I did that, doesn't help here.

Here's my JS for the grid.

01.$(document).ready(function () {
02.    var crudServiceBaseUrl = "http://localhost:56291/",
03.        dataSource = new kendo.data.DataSource({
04.            transport: {
05.                read: {
06.                    url: crudServiceBaseUrl + 'Orders/GetLineItemsForOrder/' + Cookies.get("ponum"),
07.                    dataType: "json",
08.                    type: 'GET',
09.                    contentType: 'application/json; charset=utf-8'
10.                },
11.                create: {
12.                    url: crudServiceBaseUrl + 'Orders/InsertLineItems/' + Cookies.get("ponum"),
13.                    dataType: "json",
14.                    type: 'POST',
15.                    contentType: 'application/json; charset=utf-8'
16.                },
17.                update: {
18.                    url: crudServiceBaseUrl + "Orders/UpdateLineItems",
19.                    dataType: "json",
20.                    type: "PUT",
21.                    contentType: 'application/json; charset=utf-8'
22.                },
23.                destroy: {
24.                    url: crudServiceBaseUrl + 'Orders/DeleteLineItem/',
25.                    datatype: "json",
26.                    type: 'DELETE',
27.                    contentType: 'application/json; charset=utf-8'
28.                },
29.                parameterMap: function (options, operation) {
30.                    if (operation !== "read" && options.models) {
31.                         
32.                        return { models: kendo.stringify(options.models) };
33.                        
34.                    }
35.                }
36.            },
37.            batch: true,
38.            pageSize: 20,
39.            schema: {
40.                model: {
41.                    id: "LineItemPK",
42.                    fields: {
43.                        LineItemPK: { editable: false, nullable: true },
44.                        Quantity: { editable: true, type: "number", format: "{0:d}" },
45.                        UnitPrice: { editable: true, type: "number", format: "{0:c2}" },
46.                        ExtPrice: { editable: false, type: "number", format: "{0:c2}" },
47.                        ProductFK: { editable: true },
48.                        ProductID: { editable: true },
49.                        UoM: { editable: true },
50.                        InvoiceNum: { editable: true },
51.                        DepFunction: { editable: true },
52.                        CostCenterFK: { editable: false },
53.                        AccountFK: { editable: false },
54.                        OrderFK: { editable: false }
55.                    }
56.                }
57.            }
58.        });
59. 
60.    $("#gridLineItems").kendoGrid({
61.        dataSource: dataSource,
62.        navigatable: true,
63.        pageable: true,
64.        toolbar: [{ name: "create", text: "Insert Line" }, { name: "cancel" }, { name: "save" }],
65.        columns: [
66.            { field: "LineItemPK", title: "LineItemPK", hidden: true },
67.            { field: "Quantity", title: "Qty", validation: { min: 0 } },
68.            { field: "UnitPrice", title: "Unit Price", validation: { min: 0 } },
69.            { field: "ExtPrice", title: "Ext. Price", editable: false, attributes: { "class": "ExtPrice" } },
70.            { field: "ProductFK", title: "Product FK" },
71.            { field: "ProductID", title: "Product ID" },
72.            { field: "UoM", title: "UoM" },
73.            { field: "InvoiceNum", title: "Invoice #" },
74.            { field: "DepFunction", title: "Dep. Funct." },
75.            { field: "CostCenterFK", title: "Cost Center", hidden: false },
76.            { field: "AccountFK", title: "G/L" },
77.            { field: "OrderFK", title: "OrderFK", editable: false, hidden: true },
78.            { command: "destroy", title: " ", width: 120 }
79.        ],
80.        editable: true,
81.        selectable: true
82.    });
83. 
84.});

 

So, this is driving me nuts. I've been playing a lot in the parametermap, such as changing the kendo.stringify to a JSON one, because that worked at my last place...doesn't work here. I then did this, in the model, just for kicks...

01.var models = {
02.    Quantity: 2,
03.    UnitPrice: 15.00,
04.    ExtPrice: 25.00,
05.    BackOrdered: 0,
06.    Received: 0,
07.    InvoiceNum: "IT-101",
08.    AccountFK: 2,
09.    DepFunction: "test dep function",
10.    CostCenterFK: 34,
11.    ProductFK: 2,
12.    OrderFK: 65,
13.    LineItemPK: 3
14.};
15.return JSON.stringify(models);

And that actually brought that "model" over to the web api end with the correct values, however, hard coded. 

 

In my webapiconfig.cs, my Register method is this.

 

01.public static void Register(HttpConfiguration config)
02.{
03.    // Web API configuration and services
04.    config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
05.    // Web API routes
06.    config.MapHttpAttributeRoutes();
07. 
08.    config.Routes.MapHttpRoute(
09.        name: "DefaultApi",
10.        routeTemplate: "api/{controller}/{id}",
11.        defaults: new { id = RouteParameter.Optional }
12.    );
13.     
14.}

On line 4, I did change that to, config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/json")); And still, no dice.

I strongly feel it's something on the front end, because when I hard coded that model, the values came over, there's communication - but when I remove it, zilch. I'm hoping someone can help because I've been pulling my hair out on this one. I'm sure it's a matter of me hitting the right combination of stuff.

Thanks in advance.

6 Answers, 1 is accepted

Sort by
0
Michael
Top achievements
Rank 1
answered on 08 Jun 2016, 11:38 AM
*Bump*
0
Michael
Top achievements
Rank 1
answered on 08 Jun 2016, 03:28 PM

I think I stumbled on the issue. 

When the model has the, "[" and "]" in it, it shows as null in the controller, which is when there's multiple records. But when it's a single record, like when I hard coded the JSON dataset, it works. Question is, why is this happening and how do I fix it?

0
Michael
Top achievements
Rank 1
answered on 08 Jun 2016, 05:36 PM

I finally got it.

return kendo.stringify(options.models);

in my parameter map but I think what was more important is that I was forgetting to wrap the model in an IEnumerable<>

public int UpdateLineItems(IEnumerable<spLineItemsGetAllForOrder_Result> models)

Then, in the code I'll just do a foreach(var x in spLineitems...) and proceed.

0
Han
Top achievements
Rank 1
answered on 14 Mar 2019, 04:19 PM

Sorry I really don't get it

I'm facing the same problem from days and still couldn't figure out how to solve it

I don't know what am I missing or Am I doing something wrong?

 

Here is the model:

    public class Employees
    {
        public Employees() { }

        //[Required]
        public int ID { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }
        public int Phone { get; set; }
        public string Job { get; set; }
        public string Department { get; set; }
    }

 

 

Html (view):

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Kendo Grid</title>

    <link rel="stylesheet" href="http://kendo.cdn.telerik.com/2019.1.220/styles/kendo.common.min.css">

    <link rel="stylesheet" href="http://kendo.cdn.telerik.com/2016.1.412/styles/kendo.rtl.min.css">
    <link rel="stylesheet" href="http://kendo.cdn.telerik.com/2016.1.412/styles/kendo.default.min.css">
    <link rel="stylesheet" href="http://kendo.cdn.telerik.com/2016.1.412/styles/kendo.mobile.all.min.css">

    <script src="http://kendo.cdn.telerik.com/2019.1.220/js/jquery.min.js"></script>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.7/angular.min.js"></script>
    <script src="http://kendo.cdn.telerik.com/2016.1.412/js/jszip.min.js"></script>

    <script src="http://kendo.cdn.telerik.com/2019.1.220/js/kendo.all.min.js"></script>
    <script src="http://cdn.kendostatic.com/2013.1.319/js/kendo.aspnetmvc.min.js"></script>

</head>
<body>
    <div id="example" ng-app="KendoDemos">
        <h3> Information </h3>
        <br />

        <div ng-controller="MyCtrl">
            <kendo-grid
                k-options="mainGridOptions"
                k-data-source="DS"
                k-selectable="true">
            </kendo-grid>
            
        </div>
    </div>

<script>


        var app = angular.module("KendoDemos", ["kendo.directives"]);
        app.controller("MyCtrl",
            function($scope) {
                $scope.DS = new kendo.data.DataSource({
                    transport: {
                        contentType: "application/json",
                        read: {
                            url: "/api/Employees",
                            dataType: "json",
                            type: "GET"
                        },
                        update: {
                            url: "/api/Employees/PutEmployees",
                            dataType: "json",
                            type: "PUT"
                        },
                        destroy: {
                            url: "/api/Employees/DeleteEmployees",
                            dataType: "json",
                            type: "DELETE"
                        },
                        create: {
                            url: "/api/Employees/PostEmployees",
                            dataType: "json",
                            type: "POST"

                        },
                        parameterMap: function(options, operation) {
                            if (operation !== "read") {
                                console.log(kendo.stringify(options.models));
                                console.log(JSON.stringify(options.models));
                                return { models: kendo.stringify(options.models) };
                            }
                        }
                    },
                    batch: true,
                    pageSize: 8,
                   
                    schema: {
                        
                        model: {
                            id: "ID",
                            fields: {
                                ID: { editable: false, nullable: false, type: "number" },
                                Name: { editable: true, nullable: false,type: "string", validation: { required: true } },
                            }

                        }
                    }

                });



                $scope.mainGridOptions = {
                    dataSource: $scope.DS,
                    toolbar: ["create"],
                    columns: [
                        { field: "ID", title: "ID", width: "180px" },
                        { field: "Name", title: "Name", width: "180px" },
                        { field: "Age", title: "Age", width: "180px" },
                        { field: "Phone", title: "Phone", width: "180px" },
                        { field: "Job", title: "Job", width: "180px" },
                        { field: "Department", title: "Department", width: "180px" },
                        { command: ["edit", "destroy"], title: "Actions", width: "250px" }
                    ],
                    editable: "popup",
                    pageable: true,



                };

            });


</script>
</body>
</html>  

 

 

 

H

    public class EmployeesController : ApiController
    {
        private EmployeesContext db = new Context();

        // GET: api/Employees
        
        public IQueryable<Employees> GetEmployees() {
            return db.Employees;
        }

        // GET: api/Employees/5
        [ResponseType(typeof(Employees))]
        public async Task<IHttpActionResult> GetEmployees(int id)
        {
            Employees employees = await db.Employees.FindAsync(id);
            if (employees == null)
            {
                return NotFound();
            }

            return Ok(employeesInfo);
        }

        // PUT: api/Employees/5
        [ResponseType(typeof(void))]
        public async Task<IHttpActionResult> PutEmployees(int id, Employees employees)
        {
            if (!ModelState.IsValid)
            {


                return BadRequest(ModelState);
            }

            if (id != employeesInfo.ID)
            {
                return BadRequest();
            }

            db.Entry(employeesInfo).State = EntityState.Modified;

            try
            {
                await db.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!EmployeesExists(id))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }

            return StatusCode(HttpStatusCode.NoContent);
        }

        // POST: api/Employees
        [ResponseType(typeof(Employees))]
        public async Task<IHttpActionResult> PostEmployees
            ([FromBody]Employees employees) 
{
            if (!ModelState.IsValid) {

              return BadRequest(ModelState);
            }

            db.Employees.Add(employees); // I'm testing this operation now and I get null values
            await db.SaveChangesAsync();
           
            return CreatedAtRoute("DefaultApi", new { id = employees.ID }, employees);
        }

        // DELETE: api/Employees/5
        [ResponseType(typeof(Employees))]
        public async Task<IHttpActionResult> DeleteEmployees(int id)
        {
            Employees employees = await db.Employees.FindAsync(id);
            if (employees == null)
            {
                return NotFound();
            }

            db.Employees.Remove(employees);
            await db.SaveChangesAsync();

            return Ok(employees);
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                db.Dispose();
            }
            base.Dispose(disposing);
        }

        private bool EmployeesExists(int id)
        {
            return db.Employees.Count(e => e.ID == id) > 0;
        }
    }
}

 

 

 

Could you please explain why I get all fields as null values ?

 

0
Han
Top achievements
Rank 1
answered on 14 Mar 2019, 04:27 PM

  private EmployeesContext db = new EmployeesContext(); 

 

I wrote this correctly in project but I miss wrote it here (sorry for that):

I appreciation any suggestions

Thank you

0
Alex Hajigeorgieva
Telerik team
answered on 18 Mar 2019, 03:42 PM
Hello, Han,

Thank you for the provided code snippets.

To ensure the controller actions are able to map the posted models from the Kendo UI Grid, you should look at the CRUD operations as if you are making a standard ajax request.

The first thing that seems to be incorrect is the sent data as opposed to the expected data:

parameterMap: function(options, operation) {
     // this will send an object { "models" : [{ "ID": 1, "Name": "John"}] }
     return { models: kendo.stringify(options.models) };
 }
 
// But the controller does not expect it, it expects a single object
 
public async Task<IHttpActionResult> PutEmployees(int id, Employees employees)

Here is an example of a CRUD data source without batch operations:

dataSource: {
   transport: {
       read: {
           url: "/api/grid"
       },
       create: {
           url: "/api/grid",
           type: "POST",
           contentType: 'application/json; charset=utf-8',
           dataType: 'json'
       },
       update: {
           url: function (options) {
               return "/api/grid/" + options.Id;
           },
           type: "PUT",
           contentType: 'application/json; charset=utf-8',
           dataType: 'json'
       },
       destroy: {
           url: function (options) {
               return "/api/grid/" + options.Id;
           },
           type: "DELETE",
           contentType: 'application/json; charset=utf-8',
           dataType: 'json'
       },
       parameterMap: function (options, operation) {
           if (operation !== "read" && options) {
               return  kendo.stringify(options);
           }
       }
   },

And the simple controller actions which work together with these operations:

// GET api/values
public List<Student> Get()
{
    return students;
}
 
// POST api/values
public List<Student> Post([FromBody]Student student)
{
    student.Id = students.Count + 1;
    students.Add(student);
    return new List<Student>() { student };
}
 
// PUT api/values/5
public List<Student> Put(int id, [FromBody]Student student)
{
    for (int i = 0; i < students.Count; i++)
    {
        if (students[i].Id == student.Id) {
            students[i] = student;
            break;
        }
    }
 
    return new List<Student>() { student };
}
 
// DELETE api/values/5
public List<Student> Delete(int id)
{
    var studentToRemove = new Student();
    for (int i = 0; i < students.Count; i++)
    {
        if (students[i].Id == id)
        {
            students.Remove(students[i]);
            studentToRemove = students[i];
            break;
        }
    }
 
    return new List<Student>() { studentToRemove };
}

I am also attaching a project here for your reference.

Finally, if a licence holder has the server-side Kendo.Mvc extensions, they can take advantage of the built-in "webapi" data source type and all the server operations layer taken care of as shown in this article:

https://docs.telerik.com/kendo-ui/controls/data-management/grid/how-to/binding/web-api-server-operations

Kind Regards,
Alex Hajigeorgieva
Progress Telerik
Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
Tags
Grid
Asked by
Michael
Top achievements
Rank 1
Answers by
Michael
Top achievements
Rank 1
Han
Top achievements
Rank 1
Alex Hajigeorgieva
Telerik team
Share this question
or