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

Kendo Grid column not showing value from @odata.type after crud-operation

5 Answers 691 Views
Grid
This is a migrated thread and some comments may be shown as answers.
Indicia
Top achievements
Rank 1
Indicia asked on 07 Feb 2018, 03:49 PM

Currently i'm running into an issue with the visibility of one column in my Kendo Grid while using AngularJS in combination with odata-v4.
When the grid initially loads all data is shown for each column, but after either an add or update action, the column showing the type doesn't display the value anymore, spefically for that record.
We resolve the odata type to a more readable version with a scope variable that contains the possible types

1.$scope.types = [
2.    { value: "#Awvn.Model.Discounts.CatalogDiscountRule", text: "Catalog" },
3.    { value: "#Awvn.Model.Discounts.ProgramFormDiscountRule", text: "Program Form" },
4.    { value: "#Awvn.Model.Discounts.ProductDiscountRule", text: "Product" },
5.];

Our Model and options definion for the grid, they both get send to a function that combines and sets required default settings for our data grid:

001.$q.all([
002.    DropDownListService.resolveDropDownList("Employers"),
003.    DropDownListService.resolveEnum("Awvn.Model.Discounts.DiscountCalculationMethod"),
004.    DropDownListService.resolveDropDownList("Catalogs"),
005.    DropDownListService.resolveComboBox("CostTypes"),
006.    DropDownListService.resolveComboBox("Products?$select=Id,Title,CatalogId"),
007.]).then(function (data) {
008.    var model = {
009.        id: "Id",
010.        fields: {
011.            Id: { type: "number" },
012.            EmployerId: { type: "number", defaultValue: Number($routeParams.employerId) ? Number($routeParams.employerId) : null, validation: { required: false } },
013.            EffectiveFromDate: { type: "indicia.date", defaultValue: new Date(new Date().setDate(new Date().getDate() + 1)) },
014.            DiscountAmount: { type: "indicia.percentage", defaultValue: 0 },
015.            ResellerFeeAmount: { type: "indicia.percentage", defaultValue: 0 },
016.            CalculationMethod: { type: "string", validation: { required: true } },
017.            Tag: { type: "string", nullable: true },
018.            ExcludedCostTypes: { type: "indicia.multiselect", entityType: "CostTypes" },
019. 
020.            CatalogId: {
021.                type: "indicia.foreignkey.nullable", validation: {
022.                    required: function (input) {
023.                        if (input.is("[name='CatalogId']") && input.val() === "" && $("select[name='Type']").val() === "#Awvn.Model.Discounts.CatalogDiscountRule") {
024.                            input.attr("data-required-msg", "This field is required for a Catalog Discount Rule.");
025.                            return false;
026.                        }
027. 
028.                        return true;
029.                    }
030.                }
031.            },
032. 
033.            ProgramFormId: {
034.                type: "number", validation: {
035.                    required: function (input) {
036.                        if (input.is("[name='ProgramFormId']") && input.val() === "" && $("select[name='Type']").val() === "#Awvn.Model.Discounts.ProgramFormDiscountRule") {
037.                            input.attr("data-required-msg", "This field is required for a Program Form Discount Rule.");
038.                            return false;
039.                        }
040. 
041.                        return true;
042.                    }
043.                }
044.            },
045. 
046.            ProductId: {
047.                type: "indicia.foreignkey.nullable", validation: {
048.                    required: function (input) {
049.                        if (input.is("[name='ProductId']") && input.val() === "" && $("select[name='Type']").val() === "#Awvn.Model.Discounts.ProductDiscountRule") {
050.                            input.attr("data-required-msg", "This field is required for a Product Discount Rule.");
051.                            return false;
052.                        }
053. 
054.                        return true;
055.                    }
056.                }
057.            },
058. 
059.            Type: { type: "string", from: "['@odata.type']", validation: { required: true } },
060.        }
061.    };
062. 
063.    var options = {
064.        expand: ["ExcludedCostTypes"],
065.        dataSource: {
066.            filter: [],
067.            sort: [{ field: "EffectiveFromDate", dir: "desc" }],
068.            sendFields: {
069.                admin: function (dataItem) {
070.                    if (dataItem["@odata.type"] === "#Awvn.Model.Discounts.CatalogDiscountRule") {
071.                        return {
072.                            create: ["@odata.type", "EmployerId", "EffectiveFromDate", "DiscountAmount", "ResellerFeeAmount", "CalculationMethod", "Tag", "CatalogId"],
073.                            update: ["Id", "@odata.type", "EmployerId", "EffectiveFromDate", "DiscountAmount", "ResellerFeeAmount", "CalculationMethod", "Tag", "CatalogId"]
074.                        };
075.                    } else if (dataItem["@odata.type"] === "#Awvn.Model.Discounts.ProgramFormDiscountRule") {
076.                        return {
077.                            create: ["@odata.type", "EmployerId", "EffectiveFromDate", "DiscountAmount", "ResellerFeeAmount", "CalculationMethod", "Tag", "ProgramFormId"],
078.                            update: ["Id", "@odata.type", "EmployerId", "EffectiveFromDate", "DiscountAmount", "ResellerFeeAmount", "CalculationMethod", "Tag", "ProgramFormId"]
079.                        };
080.                    } else if (dataItem["@odata.type"] === "#Awvn.Model.Discounts.ProductDiscountRule") {
081.                        return {
082.                            create: ["@odata.type", "EmployerId", "EffectiveFromDate", "DiscountAmount", "ResellerFeeAmount", "CalculationMethod", "Tag", "ProductId"],
083.                            update: ["Id", "@odata.type", "EmployerId", "EffectiveFromDate", "DiscountAmount", "ResellerFeeAmount", "CalculationMethod", "Tag", "ProductId"]
084.                        };
085.                    }
086.                }
087.            }
088.        },
089.        dataGrid: {
090.            toolbar: ['create', {
091.                name: "edit",
092.                title: "Bulk Copy",
093.                template: '<a title="Bulk copy discount rules for the current selection or (when no specific rows are selected) for all (!) rules matching the current filter." ng-click="bulkEdit()" class="k-button k-button-icontext k-grid-edit"><span class="k-icon k-i-copy"></span>Bulk Copy</a>'
094.            }, {
095.                name: "delete",
096.                title: "Bulk Delete",
097.                template: '<a title="Bulk delete discount rules for the current selection or (when no specific rows are selected) for all (!) rules matching the current filter." ng-click="bulkDelete()" class="k-button k-button-icontext k-grid-edit"><span class="k-icon k-i-delete"></span>Bulk Delete</a>'
098.            }],
099.            columns: [
100.                { title: "", template: '<input class="checkbox" type="checkbox" ng-click="onClickCheckbox(dataItem.Id)" />', width: 28 },
101.                { field: "Tag", title: "Tag" },
102.                { field: "EffectiveFromDate", title: "Effective From", width: "150px" },
103.                {
104.                    field: "Type", title: "Type", values: $scope.types, width: 180,
105.                    dependencies: [
106.                        { condition: function (val) { return val === "#Awvn.Model.Discounts.CatalogDiscountRule" }, fields: ["CatalogId"] },
107.                        { condition: function (val) { return val === "#Awvn.Model.Discounts.ProgramFormDiscountRule" }, fields: ["ProgramFormId"] },
108.                        { condition: function (val) { return val === "#Awvn.Model.Discounts.ProductDiscountRule" }, fields: ["ProductId", "CatalogId"] }
109.                    ]
110.                },
111.                { field: "DiscountAmount", title: "Discount", width: 180, },
112.                { field: "ResellerFeeAmount", title: "Reseller Fee", width: 180 },
113.                { field: "CalculationMethod", title: "CalculationMethod", values: data[1] },
114.                { field: "CatalogId", title: "Catalog", values: data[2], hidden: true },
115.                { field: "ProductId", title: "Product", hidden: true },
116.                { field: "ProgramFormId", title: "Program Form", hidden: true },
117.                { field: "ExcludedCostTypes", title: "Excluded Cost Types", values: data[3] },
118.            ],
119.            editable: {
120.                mode: "popup",
121.                template: kendo.template($("#discountRulesPopup").html())
122.            }
123.        }
124.    };

 

In our model we have to use "from: "['@odata.type']", to get the data to the Type field out of the odata-v4 attribute. We also have to use the formatting with the square brackets, if we don't use this the code won't work. I've also tried using a template for the values but I couldn't manage to get it to work that way either. With some research i also discovered that the return might be an issue with not giving all data but i also get the fully filled record returned from the server see below

01.{
02.    "@odata.context": "https://localhost/Awvn.Admin.Api/odata/$metadata#DiscountRules/Awvn.Model.Discounts.CatalogDiscountRule/$entity",
03.    "@odata.type": "#Awvn.Model.Discounts.CatalogDiscountRule",
04.    "Id": 18,
05.    "Tag": "fdfdsfds",
06.    "EmployerId": 1,
07.    "EffectiveFromDate": "2018-02-06T13:33:54.108Z",
08.    "DiscountAmount": 0,
09.    "ResellerFeeAmount": 0.01,
10.    "CalculationMethod": "DiscountFirst",
11.    "CatalogId": 1
12.}

To summarize, the @odata.type value is being shown in the grid column on an initial load, but after the a crud action, the type for the effected row disappears.

5 Answers, 1 is accepted

Sort by
0
Stefan
Telerik team
answered on 09 Feb 2018, 08:15 AM
Hello, Indicia,

Thank you for the details.

As the configuration is a complex one I can give some pointers on where we can start investigating the issue.

1) When the update action is called, please use the dataBound event the log the following details.

// log the current data
 
e.sender.dataSource.data() //observe if there is any difference in the data on load and after update
 
// Log the options of the Grid
 
e.sender.options.columns // observe if the column structure is the same on load and after update
e.sender.dataSource // observe the schema.model configuration

2) Add a column template function to that column and log the values, this will show the actual value which is passed to the column initially and after the update:

https://docs.telerik.com/kendo-ui/api/javascript/ui/grid/configuration/columns.template

I hope these tests could give us more details on what is actually causing the issue.

Also, providing an example is always very helpful as we can debug it locally and provide a suggestion best suited for it.

Regards,
Stefan
Progress Telerik
Try our brand new, jQuery-free Angular components built from ground-up which deliver the business app essential building blocks - a grid component, data visualization (charts) and form elements.
0
Indicia
Top achievements
Rank 1
answered on 09 Feb 2018, 10:40 AM

Hello Stefan,

Thank you for the tips for getting some extra information. I've ran the different tests you adviced and for the most part I didn't find any diferences between the on load and after update models. Below the results for the first step you adviced.

//e.sender.dataSource.data()
//onLoad
@odata.type : "#Awvn.Model.Discounts.CatalogDiscountRule"
Type : undefined
 
//onUpdate
@odata.type : "#Awvn.Model.Discounts.CatalogDiscountRule"
Type : undefined
 
//e.sender.options.columns
//onLoad
{field: "Type", title: "Type", values: Array(3), width: 180, dependencies: Array(3)}
 
//onUpdate
{field: "Type", title: "Type", values: Array(3), width: 180, dependencies: Array(3)}
 
//e.sender.dataSource
//onLoad
//dataSource.options.schema.model.fields
Type : {type: "string", from: "['@odata.type']", validation: {…}, parse: ƒ}
//dataSource.reader.model.fields
Type : {type: "string", from: "['@odata.type']", validation: {…}, parse: ƒ}
 
//onUpdate
//dataSource.options.schema.model.fields
Type : {type: "string", from: "['@odata.type']", validation: {…}, parse: ƒ}
//dataSource.reader.model.fields
Type : {type: "string", from: "['@odata.type']", validation: {…}, parse: ƒ}

I did however find a difference when following your second step of logging through the column template function

//column template function  console.log(dataItem)
//onLoad
@odata.type : "#Awvn.Model.Discounts.CatalogDiscountRule"
Type : "#Awvn.Model.Discounts.CatalogDiscountRule"
 
//onUpdate
@odata.type : "#Awvn.Model.Discounts.CatalogDiscountRule"
Type : undefined

 

It seems, that after the update, the value doesn't seem to be looking at the models defined from attribute, as the @odata.type value still remains present in the data item, but the Type attribute is now undefined

var model = {
    id: "Id",
    fields: {
        //I left out the other fields to keep it a bit more compact
        Type: { type: "string", from: "['@odata.type']", validation: { required: true } },
    }
};

I hope this might provide some extra information. 

Best regards

0
Angel Petrov
Telerik team
answered on 13 Feb 2018, 11:39 AM
Hi,

We have logic which strips any properties with name that contain @odata(the code for this can be found here). Please try using a custom function for the schema.data setting which calls this stripping logic and let us know of the results.

If the above does not prove helpful please try isolating the problem in a dojo which we can debug.

Regards,
Angel Petrov
Progress Telerik
Try our brand new, jQuery-free Angular components built from ground-up which deliver the business app essential building blocks - a grid component, data visualization (charts) and form elements.
0
Indicia
Top achievements
Rank 1
answered on 14 Feb 2018, 12:22 PM

Hello

Using the schema data wasn't a succes, i encountered alot of problems with the model definition on custom update functions that completly lost the model or functions usually available to it.

I've created a dojo and recreated what we have with a smaller set and a public api that also gives the @odata.type for one of the records. However, i can't really create the update function that well as it requires to generate a key for usage. I hope this might already help with something for you to debug

http://dojo.telerik.com/emEvu/2

Best regards

0
Angel Petrov
Telerik team
answered on 16 Feb 2018, 09:37 AM
Hello,

I am not sure how the service is configured and what headers should be used for the update however here is a dojo which demonstrates a possible implementation of the update. The example does not work fully however it shows how to request the correctly URL and pass the needed parameters. It is also important that the success promise is resolved after the response is returned.

Regards,
Angel Petrov
Progress Telerik
Try our brand new, jQuery-free Angular components built from ground-up which deliver the business app essential building blocks - a grid component, data visualization (charts) and form elements.
Tags
Grid
Asked by
Indicia
Top achievements
Rank 1
Answers by
Stefan
Telerik team
Indicia
Top achievements
Rank 1
Angel Petrov
Telerik team
Share this question
or