Binding CDS Data using Knockout (more tips)

This post is second in a series of querying CDS entity data and displaying it in a model-driven app web resource. A few days back, I wrote first post in this series CDS Data Binding using Knockout. Here I m answering a couple of questions and sharing a few more tips.

Though I have fetched data using WebApi and J-Query but any mechanism that returns object array will work. Yes, we can use FetchXml for querying data too.

The second thing is we can query data from multiple related entities using expand oData function. In this scenario, query returns cases with customer details. Customer can be an account or a contact:

var query = "/api/data/v9.1/incidents?$select=title&$expand=customerid_account($select=name),customerid_contact($select=fullname)";

While doing data binding a few times, I got an error:

Cannot ready property ‘name’ of undefined

The reason was in data few records don’t have customerid_account (parent object was null). To solve this, I have used if binding (Knockout offer different bindings) which checks and only bind the data if the object is not null.

<td>
<!--ko if:customerid_account -->
  <span id="accountNamee" data-bind="text:customerid_account.name"> </span>
<!--/ko-->
</td>

Knockout ‘with’ binding can also be used for this:

<td>
 <!--ko with:customerid_contact -->
         <span id="contactName" data-bind="text:fullname"></span>
 <!--/ko-->
</td>

We can do few tricks by using if and ifnot (else) bindings too. Query above is perfect example we either have customerid_account or customerid_contact not both. My requirement was to display combined data from both fields as one:

Here is complete sample code:

<!DOCTYPE html>
<html>
<head>

    https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js
    https://ajax.aspnetcdn.com/ajax/knockout/knockout-3.1.0.js
    http://../../../ClientGlobalContext.js.aspx
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">

    <script>
        $(document).ready(function () {           
            var query = "/api/data/v9.1/incidents?$select=title&$expand=customerid_account($select=name),customerid_contact($select=fullname)";
            var oDataUrl = top.Xrm.Page.context.getClientUrl() + query;
            var _data = null;

            $.getJSON(oDataUrl)
                .done(function (data) {
                    if (data != undefined && data.value.length > 0) {
                        ko.applyBindings(new AppViewModel(data));

                        debugger;
                    }
                })
                .fail(function (error) {
                    debugger;
                });

        });


        function AppViewModel(data) {


            var self = this;
            self.Results = ko.observableArray([]);

            ko.utils.arrayForEach(data.value, function (d) {

                self.Results.push(d);

            });

        }



    </script>
</head>
<body>

    <div style="overflow-x:auto;">
        <h4 class="text-center">List of Cases</h4>
        <table id="tblContainer" class="table table-sm table-striped table-hover table-borderless">
            <thead>
            <th>Title</th>
            <th>Account</th>
            <th>Contact</th>
            <th>Client</th>
            </thead>

            <tbody data-bind="foreach: Results">
                <tr>
                    <td><span id="title" data-bind="text:title"></span></td>
                    <td>
                        <!--ko if:customerid_account -->
                        <span id="accountNamee" data-bind="text:customerid_account.name"></span>
                        <!--/ko-->
                    </td>

                    <td>
                        <!--ko with:customerid_contact -->
                        <span id="contactName" data-bind="text:fullname"></span>
                        <!--/ko-->
                    </td>
                    <td>
                        <!--ko if:customerid_account -->
                        <span id="accountNamee" data-bind="text:customerid_account.name"></span>
                        <!--/ko-->
                        <!--ko with:customerid_contact -->
                        <span id="contactName" data-bind="text:fullname"></span>
                        <!--/ko-->
                    </td>


                </tr>
            </tbody>
        </table>

    </div>


</body>
</html>

I hope you find this useful.

Let’s Connect

 twIcon lnIcon fbicon

CDS Data Binding using Knockout

In this post, I am explaining how to query data from a CDS entity and use Knockout to bind it in a model-driven app web resource. It has the following advantages:

  1. It is easy don’t require a lot of efforts (less coding)
  2. We have the flexibility to do further customisation

About demo:

Since it is demo, I have included script and link references from CDN. The focus of this post is to explain and share sample code for data binding. I m loading my web resource as a popup and have used AlertJS‘s free version for this. To keep this post short, I m not explaining how to add a button in Model-Driven App. But to summarise steps I have added JS file in my form, customised ribbon and added a button with the following code.

"use strict";
///<reference path="../mag_/js/alert.js">
var Account = (function ()
{
	return {
		LoadAccountsPopup: function (executionContext)
		{   
            Alert.showWebResource("webresource_accounts");			 
		}
	}
})();

In my web resource, I m getting data using Web API query. Data binding is done of course using Knockout and have used Bootstrap for styling. Here is code:

<!DOCTYPE html>
<html>
<head>

    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script src="https://ajax.aspnetcdn.com/ajax/knockout/knockout-3.1.0.js" type="text/javascript"></script>
    <script src="ClientGlobalContext.js.aspx" type="text/javascript"></script>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">

    <script>
        $(document).ready(function () {
            var query = "/api/data/v9.1/accounts?$top=22&$select=name,telephone1,address1_city";
            var oDataUrl = parent.Xrm.Page.context.getClientUrl() + query;          

            $.getJSON(oDataUrl)
                .done(function (data) {
                    if (data != undefined && data.value.length > 0) {
                        ko.applyBindings(new AppViewModel(data));

                        debugger;
                    }
                })
                .fail(function (error) {
                    debugger;
                });

        });
        function AppViewModel(data) {
            var self = this;
            self.pingResults = ko.observableArray([]);
            ko.utils.arrayForEach(data.value, function (d) {
            self.pingResults.push(d);
            });
        }
    </script>
</head>
<body>

    <div style="overflow-x:auto;">
        <h4 class="text-center">List of Accounts</h4>
        <table id="tblContainer" class="table table-sm table-striped table-hover table-borderless">
            <thead>
            <th>Account Name</th>
            <th>Telephone</th>
            <th>City</th>

            </thead>

            <tbody data-bind="foreach: pingResults">
                <tr>
                    <td><span id="fileName" data-bind="text:name"></span></td>
                    <td><span id="fileType" data-bind="text:telephone1"></span></td>
                    <td><span id="fileType" data-bind="text:address1_city"></span></td>
                </tr>
            </tbody>
        </table>
    </div>
</body>
</html>

I hope you find this helpful. If you have a question please leave me a comment on my blog and I will try to assist.

Enjoy your day.

Let’s Connect

 twIcon lnIcon fbicon

Web API “An error occurred while validating input parameters”

Today I had this error in my JS Web Api request:

{“error”:{“code”:”0x0″,”message”:”An error occurred while validating input parameters: Microsoft.OData.ODataException: An undeclared property ‘mm_account’ which only has property annotations in the payload but no property value was found in the payload. In OData, only declared navigation properties and declared named streams can be represented as properties without values.

The code was generated through CRM REST Builder, which is a fantastic tool developed by Jason Lattimer MVP for generating JS requests.

The particular request was updating an entity field. Field’s name was “mm_account” and schema name was “mm_Account”. Finding was for some reason generated code was using field name instead of schema name. Changing name fixed this issue.

I hope sharing this will save someone’s time 🙂

Let’s Connect

 twIcon lnIcon fbicon