Kleber Predictive Address Capture is a service provided by DataTools. In this article, I will walk you through, how to create a custom web resource to integrate with Kleber and also provide the predictive address search user experience on a Dynamics 365 (CRM) form.
The predictive address search is a two step process.
- Search for a set of addresses based on the partially entered text.
- Read the full address details of the selected address
Below diagram details the complete process.
Resources
You would need to download the below JavaScript and CSS files.
jQuery – https://code.jquery.com/jquery-1.9.1.js
jQuery UI (the version I used is jQuery UI Core 1.10.2) – http://jqueryui.com/download/
All the source files I used can be found in below GitHub repository.
https://github.com/dyn365apps/kleberdyn365
Source Code
HTML and CSS
We need to create a HTML web resource which includes an input element to collect partial address text, JavaScript to call the Kleber Services, and CSS to update the UI to match Dynamics 365 (CRM) UI.
HTML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<body style="margin: 0px; word-wrap: break-word;"> <form style="margin-bottom: 0px;"> <table id="allfields" border="0" cellspacing="0" cellpadding="0" width="100%" style="margin-top: 2px"> <tbody> <tr> <td style="font-family: Segoe\000020UI,Tahoma,Arial; font-size: 12px; width:150; max-width: 150px; text-align: left; color: #444444; padding: 0px;">Postal Address</td> <td style="font-family: Segoe\000020UI,Tahoma,Arial; font-size: 12px; width:auto; text-align: left; color: #444444; padding: 0px; padding-left: 26px;"> <input style="font-family: Segoe\000020UI,Tahoma,Arial; font-size: 12px; font-weight: bold; color: #000000; width:100%; margin-left:1px; height: 24px;" class="textbox ui-autocomplete-input" size="50" name="Address line 1" id="addline" autocomplete="off" type="text" value="--"> <span role="status" aria-live="polite" class="ui-helper-hidden-accessible">10 results are available, use up and down arrow keys to navigate.</span> </td> </tr> </tbody> </table> </form> </body> |
CSS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
<style> #addline { border: 0px solid; background-color: #ffffff; height: 24px; } #addline:focus { outline: 1px solid #CCCCCC; background-color: #ffffff; padding-left: 5px; } #addline:hover { outline: 1px solid #CCCCCC; outline-offset: -2px; background-color: #eeeeee; padding-left: 5px; } #addline:active { outline: 1px solid #CCCCCC; background-color: #ffffff; padding-left: 5px; } </style> |
GetRequestKey
Every subscribed user has a unique RequestKey. You need to login to your Kleber account, and you can get your RequestKey. This function simply returns the RequestKey when called.
1 2 3 4 |
function getRequestKey() { var RequestKey = "RK-33333-33333-44444-55555-55555-44444-33333-33333"; return RequestKey; } |
LoadAddressOnForm
LoadAddressOnForm is called inside $(document).ready. This will read the values in Dynamics 365 (CRM) address fields, concatenate the fields, and add to addline input element.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
dyn365appsAddressMethods.LoadAddressOnForm = function () { var address = "--"; if (parent.Xrm.Page.getAttribute("address1_line1").getValue() != null) { address = parent.Xrm.Page.getAttribute("address1_line1").getValue(); } if (parent.Xrm.Page.getAttribute("address1_city").getValue() != null) { address += ", " + parent.Xrm.Page.getAttribute("address1_city").getValue(); } if (parent.Xrm.Page.getAttribute("address1_stateorprovince").getValue() != null) { address += ", " + parent.Xrm.Page.getAttribute("address1_stateorprovince").getValue(); } if (parent.Xrm.Page.getAttribute("address1_postalcode").getValue() != null) { address += ", " + parent.Xrm.Page.getAttribute("address1_postalcode").getValue(); } if (parent.Xrm.Page.getAttribute("address1_country").getValue() != null) { address += ", " + parent.Xrm.Page.getAttribute("address1_country").getValue(); } $('#addline').val(address); }; |
DataTools.Capture.Address.Predictive.AuPaf.SearchAddress
When the user starts typing the address, DataTools.Capture.Address.Predictive.AuPaf.SearchAddress Method is called to get a list of matching addresses. You can limit the numbers addresses returned by changing the ResultLimit parameter.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
source: function (request, response) { $.ajax( { url: "https://kleber.datatoolscloud.net.au/KleberWebService/DtKleberService.svc/ProcessQueryStringRequest", dataType: "jsonp", type: "GET", contentType: "application/json; charset=utf-8", data: { OutputFormat: "json", ResultLimit: 4, AddressLine: request.term, Method: "DataTools.Capture.Address.Predictive.AuPaf.SearchAddress", RequestKey: TempKey }, success: function (data) { response($.map(data.DtResponse.Result, function (item) { var Output = (item.AddressLine + ", " + item.Locality + ", " + item.State + ", " + item.Postcode); return { label: Output, value: Output, Output: Output, RecordId: item.RecordId, AddressLine: item.AddressLine }; })); } }); } |
DataTools.Capture.Address.Predictive.AuPaf.RetrieveAddress
When you click on one of the returned addresses, DataTools.Capture.Address.Predictive.AuPaf.RetrieveAddress Method is called to get full address details. Once, the full address details are retrieved, the Dynamics 365 (CRM) form’s address fields are populated with the retrieve addresses components. User then saves the record.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
select: function (event, ui) { $.ajax( { url: "https://kleber.datatoolscloud.net.au/KleberWebService/DtKleberService.svc/ProcessQueryStringRequest", dataType: "jsonp", crossDomain: true, data: { OutputFormat: "json", RecordId: ui.item.RecordId, Method: "DataTools.Capture.Address.Predictive.AuPaf.RetrieveAddress", RequestKey: TempKey }, success: function (data) { $.map(data.DtResponse.Result, function (item) { parent.Xrm.Page.getAttribute("address1_line1").setValue(ui.item.AddressLine); parent.Xrm.Page.getAttribute("address1_city").setValue(item.Locality); parent.Xrm.Page.getAttribute("address1_stateorprovince").setValue(item.State); parent.Xrm.Page.getAttribute("address1_postalcode").setValue(item.Postcode); parent.Xrm.Page.getAttribute("address1_country").setValue("AUSTRALIA"); parent.Xrm.Page.getAttribute("dyn365apps_dpid").setValue(item.DPID); parent.Xrm.Page.getAttribute("dyn365apps_addressvalidated").setValue("425320001"); // Valid Address var address = parent.Xrm.Page.getAttribute("address1_line1").getValue(); address += ", " + parent.Xrm.Page.getAttribute("address1_city").getValue(); address += ", " + parent.Xrm.Page.getAttribute("address1_stateorprovince").getValue(); address += ", " + parent.Xrm.Page.getAttribute("address1_postalcode").getValue(); address += ", " + parent.Xrm.Page.getAttribute("address1_country").getValue(); $('#addline').val(address); }); } }); } |
Issue
There was an issue, when users type a partial address and hit enter key. The web resource showed 404 error. To resolve this below fix was implemented.
1 2 3 4 5 6 7 8 9 10 11 12 |
$(document).ready(function (e) { dyn365appsAddressMethods.LoadAddressOnForm(); <span style="color: #ff0000;"> // Stop form submission on enter key press $(window).keydown(function (event) { if (event.keyCode == 13) { event.preventDefault(); return false; } });</span> }); |
Add Web Resource to Dynamics 365 (CRM) Form
Create a HTML web resource record in Dynamics 365 (CRM) and include the complete code as shown below.
Add the web resource to the form.
Save and Publish.
Conclusion
Once published, you can open the form (in my example, I used the Lead form) and start typing inside the input element. The result should look like this.
Complete source code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>dyn365apps - Address Validation</title> <link href="/WebResources/dyn365apps_autocomplete_style.css" rel="stylesheet"> <script src="/WebResources/dyn365apps_jquery1.9.1.js"></script> <script src="/WebResources/dyn365apps_jquery_autocomplete_all.js"></script> <script> function getRequestKey() { var RequestKey = "RK-33333-33333-44444-55555-55555-44444-33333-33333"; return RequestKey; } dyn365appsAddressMethods = new Object(); dyn365appsAddressMethods.LoadAddressOnForm = function () { var address = "--"; if (parent.Xrm.Page.getAttribute("address1_line1").getValue() != null) { address = parent.Xrm.Page.getAttribute("address1_line1").getValue(); } if (parent.Xrm.Page.getAttribute("address1_city").getValue() != null) { address += ", " + parent.Xrm.Page.getAttribute("address1_city").getValue(); } if (parent.Xrm.Page.getAttribute("address1_stateorprovince").getValue() != null) { address += ", " + parent.Xrm.Page.getAttribute("address1_stateorprovince").getValue(); } if (parent.Xrm.Page.getAttribute("address1_postalcode").getValue() != null) { address += ", " + parent.Xrm.Page.getAttribute("address1_postalcode").getValue(); } if (parent.Xrm.Page.getAttribute("address1_country").getValue() != null) { address += ", " + parent.Xrm.Page.getAttribute("address1_country").getValue(); } $('#addline').val(address); }; $(document).ready(function (e) { dyn365appsAddressMethods.LoadAddressOnForm(); // Stop form submission on enter key press $(window).keydown(function (event) { if (event.keyCode == 13) { event.preventDefault(); return false; } }); }); $(function () { var TempKey = getRequestKey(); $('#addline').autocomplete( { source: function (request, response) { $.ajax( { url: "https://kleber.datatoolscloud.net.au/KleberWebService/DtKleberService.svc/ProcessQueryStringRequest", dataType: "jsonp", type: "GET", contentType: "application/json; charset=utf-8", data: { OutputFormat: "json", ResultLimit: 4, AddressLine: request.term, Method: "DataTools.Capture.Address.Predictive.AuPaf.SearchAddress", RequestKey: TempKey }, success: function (data) { response($.map(data.DtResponse.Result, function (item) { var Output = (item.AddressLine + ", " + item.Locality + ", " + item.State + ", " + item.Postcode); return { label: Output, value: Output, Output: Output, RecordId: item.RecordId, AddressLine: item.AddressLine }; })); } }); }, select: function (event, ui) { $.ajax( { url: "https://kleber.datatoolscloud.net.au/KleberWebService/DtKleberService.svc/ProcessQueryStringRequest", dataType: "jsonp", crossDomain: true, data: { OutputFormat: "json", RecordId: ui.item.RecordId, Method: "DataTools.Capture.Address.Predictive.AuPaf.RetrieveAddress", RequestKey: TempKey }, success: function (data) { $.map(data.DtResponse.Result, function (item) { parent.Xrm.Page.getAttribute("address1_line1").setValue(ui.item.AddressLine); parent.Xrm.Page.getAttribute("address1_city").setValue(item.Locality); parent.Xrm.Page.getAttribute("address1_stateorprovince").setValue(item.State); parent.Xrm.Page.getAttribute("address1_postalcode").setValue(item.Postcode); parent.Xrm.Page.getAttribute("address1_country").setValue("AUSTRALIA"); parent.Xrm.Page.getAttribute("dyn365apps_dpid").setValue(item.DPID); parent.Xrm.Page.getAttribute("dyn365apps_addressvalidated").setValue("425320001"); // Valid Address var address = parent.Xrm.Page.getAttribute("address1_line1").getValue(); address += ", " + parent.Xrm.Page.getAttribute("address1_city").getValue(); address += ", " + parent.Xrm.Page.getAttribute("address1_stateorprovince").getValue(); address += ", " + parent.Xrm.Page.getAttribute("address1_postalcode").getValue(); address += ", " + parent.Xrm.Page.getAttribute("address1_country").getValue(); $('#addline').val(address); }); } }); }, }); }); </script> <style> #addline { border: 0px solid; background-color: #ffffff; height: 24px; } #addline:focus { outline: 1px solid #CCCCCC; background-color: #ffffff; padding-left: 5px; } #addline:hover { outline: 1px solid #CCCCCC; outline-offset: -2px; background-color: #eeeeee; padding-left: 5px; } #addline:active { outline: 1px solid #CCCCCC; background-color: #ffffff; padding-left: 5px; } </style> <meta> <meta> <meta> <meta> </head> <body style="margin: 0px; word-wrap: break-word;"> <form style="margin-bottom: 0px;"> <table id="allfields" border="0" cellspacing="0" cellpadding="0" width="100%" style="margin-top: 2px"> <tbody> <tr> <td style="font-family: Segoe\000020UI,Tahoma,Arial; font-size: 12px; width:150; max-width: 150px; text-align: left; color: #444444; padding: 0px;">Postal Address</td> <td style="font-family: Segoe\000020UI,Tahoma,Arial; font-size: 12px; width:auto; text-align: left; color: #444444; padding: 0px; padding-left: 26px;"> <input style="font-family: Segoe\000020UI,Tahoma,Arial; font-size: 12px; font-weight: bold; color: #000000; width:100%; margin-left:1px; height: 24px;" class="textbox ui-autocomplete-input" size="50" name="Address line 1" id="addline" autocomplete="off" type="text" value="--"> <span role="status" aria-live="polite" class="ui-helper-hidden-accessible">10 results are available, use up and down arrow keys to navigate.</span> </td> </tr> </tbody> </table> </form> </body> </html> |
Thank you for visiting Dyn365Apps.com.
Follow me on Twitter to get the latest news, tips and tricks and more …
Until next time.