In this blog post I will not go into detail in how to install all the pre-requisites that are required to build and run PCF controls. My goal was to build a new PCF control and get into coding of PCF controls as fast as possible.
Here are a few links to articles that will help you installing the pre-requisites (Microsoft PowerApps CLI) https://docs.microsoft.com/en-us/powerapps/developer/component-framework/get-powerapps-cli
Other good references to get into this topic:
https://toddbaginski.com/blog/how-to-create-a-powerapps-pcf-control/
https://docs.microsoft.com/en-us/powerapps/developer/component-framework/create-custom-controls-using-pcf
I looked through the Guido Preite’s https://pcf.gallery/ which will help you find appropriate use cases / examples for your own needs. It did not take very long to find a simple example to start with: Andrew Butenko's https://pcf.gallery/address-autocomplete/
A few moments later I had the idea to create yet another address autocomplete control but this time powered by the Bing API.
With the Field Service App license you receive the auto-geocode feature and the geocode button for entities like account or work order. This feature also relies on the Bing API. My idea - using this key for the new Bing auto complete control should do job to make it work without buying an extra license for a Google Maps API Key.
For the next step I researched on how to create a PCF control without doing too much typing in a command prompt console. A very nice and nifty solution is the XrmToolBox plugin "PCF Custom Control Builder" developed by Danish Naglekar. In his blog post he explains more details on his plugin:
https://danishnaglekar.wordpress.com/2019/10/07/pcf-custom-control-builder/
I entered in all the necessary data and followed the steps to build and create this new Bing address autocomplete control:
General
1. Control Location: Path of the control project with all dependencies, etc.
2. Visual Studio Command Prompt Location: Path to VS 2017 CMD
Component Details
Here is the log of my solution create, add and build statements…
Once deployed and published you can go to your entity of choice and configure the control similar to pictures shown below.
The goal is to use the "Name" field to enter an address and to have the Bing autocomplete feature as support to resolve and propose a valid address.
So I open the change properties dialog for this field and configure to use the Bing Address Autocomplete PCF control:
Here I chose to use my new control for Web, Phone and Tablet.
Then I linked the entity’s address fields to my control properties.
Last step is to enter a static value for the Bing Api Key.
If done, save and publish the changes on the entity form and you are ready to go to try it out.
Once we select an address it will automatically fill in the previously mapped fields of this control and at the end it will look like this:
Here are a few links to articles that will help you installing the pre-requisites (Microsoft PowerApps CLI) https://docs.microsoft.com/en-us/powerapps/developer/component-framework/get-powerapps-cli
Other good references to get into this topic:
https://toddbaginski.com/blog/how-to-create-a-powerapps-pcf-control/
https://docs.microsoft.com/en-us/powerapps/developer/component-framework/create-custom-controls-using-pcf
I looked through the Guido Preite’s https://pcf.gallery/ which will help you find appropriate use cases / examples for your own needs. It did not take very long to find a simple example to start with: Andrew Butenko's https://pcf.gallery/address-autocomplete/
A few moments later I had the idea to create yet another address autocomplete control but this time powered by the Bing API.
Bing Api Key
Reason for that decision is that with a paid Customer Engagement plan or Field Service App license you get the ability to use the D365 instance's Bing API Key, which can be found under Resource Scheduling => Settings => Administration => Scheduling Parameters.With the Field Service App license you receive the auto-geocode feature and the geocode button for entities like account or work order. This feature also relies on the Bing API. My idea - using this key for the new Bing auto complete control should do job to make it work without buying an extra license for a Google Maps API Key.
For the next step I researched on how to create a PCF control without doing too much typing in a command prompt console. A very nice and nifty solution is the XrmToolBox plugin "PCF Custom Control Builder" developed by Danish Naglekar. In his blog post he explains more details on his plugin:
https://danishnaglekar.wordpress.com/2019/10/07/pcf-custom-control-builder/
I entered in all the necessary data and followed the steps to build and create this new Bing address autocomplete control:
General
1. Control Location: Path of the control project with all dependencies, etc.
2. Visual Studio Command Prompt Location: Path to VS 2017 CMD
Component Details
- Namespace
- Control Name
- Template (optional): e.g. from an existing PCF Gallery’s control
- Version: Auto-Incremented
- Create Button: Creates initial control structure
- Open in VS Code: will open the project’s TypeScript source file in Visual Studio Code Editor
- Build: Builds the control
- Test will run and open a browser instance with a self-hosted test kit containing your PCF control
- Initialize CDS project: Define you Solution name, Publisher name and prefix. Currently, the plugin does not allow spaces in the names.
- Deploy to D365 CE: Once the custom control is ready, deploy the control directly in you D365 CE or CDS environment using XrmToolbox connection
Here is the log of my solution create, add and build statements…
Create the solution
Add a control reference to the solution
Rebuild and bundling of the control from the CDS project
Deployment & Configuration
Via the "Deploy" button you can easily deploy your freshly made CDS solution to your D365 instance.Once deployed and published you can go to your entity of choice and configure the control similar to pictures shown below.
This is my test entity "Main Address"
The goal is to use the "Name" field to enter an address and to have the Bing autocomplete feature as support to resolve and propose a valid address.
So I open the change properties dialog for this field and configure to use the Bing Address Autocomplete PCF control:
Add control to the Name field
Configure the Bing Address Autocomplete control
The configuration allows you to control on which device you can see and use available controls.Here I chose to use my new control for Web, Phone and Tablet.
Then I linked the entity’s address fields to my control properties.
Last step is to enter a static value for the Bing Api Key.
If done, save and publish the changes on the entity form and you are ready to go to try it out.
Final result
On change of the name field the autocomplete control will trigger the dropdown list with suggestions.Once we select an address it will automatically fill in the previously mapped fields of this control and at the end it will look like this:
In Action
The implementation of the control
/// <reference path="types/MicrosoftMaps/Modules/Autosuggest.d.ts" /> import { IInputs, IOutputs } from "./generated/ManifestTypes"; export class BingAddressAutocomplete implements ComponentFramework.StandardControl<IInputs, IOutputs> { private notifyOutputChanged: () => void; private searchBox: HTMLInputElement; private value: string; private street: string; private city: string; private county: string; private state: string; private zipcode: string; private country: string; constructor() { } public init(context: ComponentFramework.Context<IInputs>, notifyOutputChanged: () => void, state: ComponentFramework.Dictionary, container: HTMLDivElement) { if (typeof (context.parameters.bingapikey) === "undefined" || typeof (context.parameters.bingapikey.raw) === "undefined") { container.innerHTML = "Please provide a valid bing api key"; return; } this.notifyOutputChanged = notifyOutputChanged; this.searchBox = document.createElement("input"); this.searchBox.setAttribute("id", "searchBox"); this.searchBox.className = "addressAutocomplete"; this.searchBox.addEventListener("mouseenter", this.onMouseEnter.bind(this)); this.searchBox.addEventListener("mouseleave", this.onMouseLeave.bind(this)); if (typeof (context.parameters.value) !== "undefined" && typeof (context.parameters.value.raw) !== "undefined" && context.parameters.value.raw != null) { this.searchBox.setAttribute("value", context.parameters.value.raw); } container.setAttribute("id", "searchBoxContainer"); container.appendChild(this.searchBox); let bingApiKey = context.parameters.bingapikey.raw; let scriptUrl = "https://www.bing.com/api/maps/mapcontrol?callback=loadAutoSuggest&key=" + bingApiKey; let scriptNode = document.createElement("script"); scriptNode.setAttribute("type", "text/javascript"); scriptNode.setAttribute("src", scriptUrl); // scriptNode.setAttribute("async", ""); // scriptNode.setAttribute("defer", ""); document.head.appendChild(scriptNode); var _this = this; window.setTimeout(() => { Microsoft.Maps.loadModule('Microsoft.Maps.AutoSuggest', { callback: () => { var options = {maxResults: 5}; var manager = new Microsoft.Maps.AutosuggestManager(options); manager.attachAutosuggest('#searchBox', '#searchBoxContainer', (suggestionResult) => { _this.street = suggestionResult.address.addressLine; _this.city = suggestionResult.address.locality; _this.county = suggestionResult.address.district; _this.state = suggestionResult.address.adminDistrict; _this.country = suggestionResult.address.countryRegion; _this.zipcode = suggestionResult.address.postalCode; _this.value = suggestionResult.formattedSuggestion || ""; _this.notifyOutputChanged(); }); }, errorCallback: () =>{alert("Error with loading of module Microsoft.Maps.AutoSuggest.");} }); }, 1000); } private selectedSuggestion(suggestionResult: Microsoft.Maps.ISuggestionResult): void { alert(suggestionResult.formattedSuggestion); this.value = ""; this.street = ""; this.city = ""; this.county = ""; this.state = ""; this.country = ""; this.zipcode = ""; this.value = suggestionResult.formattedSuggestion || ""; this.notifyOutputChanged(); } private onMouseEnter(): void { this.searchBox.className = "addressAutocompleteFocused"; } private onMouseLeave(): void { this.searchBox.className = "addressAutocomplete"; } /** * Called when any value in the property bag has changed. This includes field values, data-sets,
* global values such as container height and width,
* offline status, control metadata values such as label, visible, etc. * @param context The entire property bag available to control via Context Object;
* It contains values as set up by the customizer mapped to names defined in the manifest, as well as utility functions */ public updateView(context: ComponentFramework.Context<IInputs>): void { // Add code to update control view } /** * It is called by the framework prior to a control receiving new data. * @returns an object based on nomenclature defined in manifest, expecting object[s] for property marked as “bound” or “output” */ public getOutputs(): IOutputs { return { value: this.value, street: this.street, city: this.city, county: this.county, state: this.state, country: this.country, zipcode: this.zipcode }; } /** * Called when the control is to be removed from the DOM tree. Controls should use this call for cleanup. * i.e. cancelling any pending remote calls, removing listeners, etc. */ public destroy(): void { // Add code to cleanup control if necessary } }
Hi, Great component.
ReplyDeleteI'm trying to add this into a test instance and I'm able to compile it and import it successfully. After setting up the control, entering an address in the main field nothing happens. I being typing in the address and nothing occurs, no dropdown list or anything....
When I run the component via XRMToolbox using "Test" - It loads locally and seems to work fine. I'm not sure whats happening. Any suggestions?
Cheers
Well that was quick - I tried one more thing, changed the layout from 3 columns to 2 and now it's working.
DeleteIt must have something to do with the narrow columns when using 3 and it won't show the dropdown list.
Fantastic!
Thanks again :)
Hi,
ReplyDeleteI was not able to map the OOB address fields but worked fine with custom fields. Am I missing something or is it a limitation ?
.NET Framework 4.6.2 Developer Pack is a requirement to build this in XRMToolKit. Just thought I would post this incase it helps someone. Didn't workw ith 4.8 version.
ReplyDeleteIf you want to add Longitude and Latitude use .location like below
ReplyDelete_this.longitude = suggestionResult.location.longitude.toString();
This comment has been removed by the author.
ReplyDeleteGood solution, is it also possible to get the country ISO code from the bing maps API so that we can complete a country lookup field? If possible, I would add this as a lot of customers use a lookup field to capture the country in a structured way and often implement some extra logic based on that country lookup...
ReplyDeleteI am facing an error when i click on build.
ReplyDeleteError -> An error occurred while updating the version. Please report an issue on github page.
I Like this control and it's working well for me. But when I try to add it more than once on the same form (even if on separate tabs), the second instance does not work. Am I missing something, or is that just how it works?
ReplyDeleteWe built the PCF , all standard field adress_1 are display in the list except the field adress1_County which is type text like the other field. Did we miss something ?
ReplyDeleteYes, I am with some issue. Why? It seams not all text fields appear as a option to bound a value.
DeleteI solve my problem editing form Xml.
DeleteI'm having a problem.
ReplyDeleteThe callback never run.
Does anyone have a ideia that can help?
window.setTimeout(function () {
Microsoft.Maps.loadModule('Microsoft.Maps.AutoSuggest', {
callback: function callback() {
var options = {
maxResults: 5
};
var manager = new Microsoft.Maps.AutosuggestManager(options);
manager.attachAutosuggest('#searchBox', '#searchBoxContainer', function (suggestionResult) {
_this.street = suggestionResult.address.addressLine;
_this.city = suggestionResult.address.locality;
_this.county = suggestionResult.address.district;
if (_this.county === undefined) {
_this.county = _this.city;
}
_this.state = suggestionResult.address.adminDistrict;
_this.country = suggestionResult.address.countryRegion;
_this.zipcode = suggestionResult.address.postalCode;
_this.value = suggestionResult.formattedSuggestion || "";
_this.notifyOutputChanged();
});
},
errorCallback: function errorCallback() {
alert("Error with loading of module Microsoft.Maps.AutoSuggest.");
}
});
}, 1000);
Thanks.
This comment has been removed by a blog administrator.
ReplyDeleteMy Build is failing with this error:
ReplyDeleteC:\Users\knati\.nuget\packages\microsoft.powerapps.msbuild.solution\1.22.4\tasks\net5.0\Microsoft.PowerPlatform.MSBuild.Telemetry.Tasks.tasks(20,5): error MSB4062: The "PpbtTelemetryInitializeSession" task could not be loaded from the assembly C:\Users\knati\.nuget\packages\microsoft.powerapps.msbuild.solution\1.22.4\tasks\net5.0\Microsoft.PowerPlatform.MSBuild.Telemetry.Tasks.dll. Could not load file or assembly 'System.Runtime, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. The system cannot find the file specified. Confirm that the declaration is correct, that the assembly and all its dependencies are available, and that the task contains a public class that implements Microsoft.Build.Framework.ITask. [D:\reivaxmik\DEV\PowerApps\PCF\PCF.BingAddressAutocomplete-master\BingAddressAutocomplete\Solutions\Solutions.cdsproj]
I have updated my .NET 4.6.2 runtime and SDK but the issue persists. Any ideas? thanks!
Bing API Key -
ReplyDeleteI am using this PCF control and need further clarity on which key need to be used. Bing Custom Search or Bing Search v7