{"id":219,"date":"2022-01-07T11:18:59","date_gmt":"2022-01-07T11:18:59","guid":{"rendered":"https:\/\/blog.citrus-lime.com\/crmc\/?p=219"},"modified":"2022-01-07T11:19:00","modified_gmt":"2022-01-07T11:19:00","slug":"pcf-custom-controls","status":"publish","type":"post","link":"https:\/\/blog.citrus-lime.com\/crmc\/pcf-custom-controls\/","title":{"rendered":"PCF Custom Controls"},"content":{"rendered":"\n<p>Even as a long standing Dynamics Developer and Architect I\u2019d held back slightly from getting into Custom PCF Development as one look at the steps involved gave me pause.<\/p>\n\n\n\n<p>This is because the initial steps looked like a decent barrier to entry \u2013 as the initial start is heavily dependent on compiling via command line\u00a0and\u00a0<a href=\"https:\/\/www.npmjs.com\/\">npm<\/a>.\u00a0<\/p>\n\n\n\n<p>The best technologies that people accustomed to \u2018bog-standard\u2019 TypeScript, Plugin Development and .Net might not be used to.<\/p>\n\n\n\n<p>But like all new technologies, its only really off-putting until you\u2019ve got stuck in and done your first Hello World style control.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Prep your Control folder<\/h3>\n\n\n\n<p>We can start building a Custom Control by creating a new Folder and then calling the following command from the Developer Powershell:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\"><p><strong>pac pcf init&nbsp;<\/strong>&#8211;namespace CRMCS.PCF.CustomControls &#8211;name ExampleControl &#8211;template field<\/p><\/blockquote>\n\n\n\n<p>This preps the Folder Structure with the right template files.<\/p>\n\n\n\n<p>As a 2nd step, we can then \u2018npm install\u2019 to setup the node_modules in the Project Folder with the relevant script files.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/license.citruslime.com\/cs\/blogs\/crmcs\/image_3EB8A6D9.png\"><img decoding=\"async\" src=\"https:\/\/license.citruslime.com\/cs\/blogs\/crmcs\/image_thumb_3969C028.png\" alt=\"image\" title=\"image\" \/><\/a><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Building the Custom Control<\/h3>\n\n\n\n<p>With our Developer PowerShell for VS2017 or VS2019 connected to the Folder for our Custom Control \u2013 we can then \u2018<strong>npm run build<\/strong>\u2019<\/p>\n\n\n\n<p>This will build our control and in doing so, find and report on any exceptions the compiler finds in our Xml Manifest or TypeScript JS.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/license.citruslime.com\/cs\/blogs\/crmcs\/image_082A39EE.png\"><img decoding=\"async\" src=\"https:\/\/license.citruslime.com\/cs\/blogs\/crmcs\/image_thumb_322F5978.png\" alt=\"image\" title=\"image\" \/><\/a><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Unit Testing the Custom Control<\/h3>\n\n\n\n<p>The default position would be compiling the Control, packaging into a Solution ZIP, and the importing into Dynamics 365 to test our control.<\/p>\n\n\n\n<p>However this would give us a slow and cumbersome way of testing our code as it develops.<\/p>\n\n\n\n<p>Thankfully there is a better way as we can deploy our Control into a Test Harness and sample our logic before using in Dynamics 365.<\/p>\n\n\n\n<p>We can do this from our Developer Powershell by running \u2018<strong>npm start<\/strong>\u2019.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/license.citruslime.com\/cs\/blogs\/crmcs\/image_718D0D08.png\"><img decoding=\"async\" src=\"https:\/\/license.citruslime.com\/cs\/blogs\/crmcs\/image_thumb_3B1EEBB7.png\" alt=\"image\" title=\"image\" \/><\/a><\/figure>\n\n\n\n<p>Note that our Control will likely fail initially until we have supplied the right Parameters for our Control in the&nbsp;<strong>Data Inputs<\/strong>&nbsp;section of the Test Harness.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\"><p><a href=\"https:\/\/docs.microsoft.com\/en-us\/powerapps\/developer\/component-framework\/debugging-custom-controls\">https:\/\/docs.microsoft.com\/en-us\/powerapps\/developer\/component-framework\/debugging-custom-controls<\/a><\/p><\/blockquote>\n\n\n\n<p>One point when using the Test Harness is that the \u2018page\u2019 object part of the context may not have your CDS\u2019s Url available via the .getClientUrl().<\/p>\n\n\n\n<p>This will be a way to supply the Test Harness with a stubby Url to mimic how the Control will be used \u2013 but until we determine how best to do this, we can use a Conditional Statement or a blunt try\/catch block to instead supply a fixed URL to our Dynamics 365 Environment during Testing with the Test Harness.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\"><p><strong>try {<\/strong><\/p><p><strong>&nbsp; this.baseUrl = (&lt;any&gt;context).page.getClientUrl();<\/strong><\/p><p><strong>}<\/strong><\/p><p><strong>catch (ex) {<\/strong><\/p><p><strong>&nbsp; this.baseUrl = &#8220;https:\/\/[mycrm].crm11.dynamics.com&#8221;;<\/strong><\/p><p><strong>}<\/strong><\/p><\/blockquote>\n\n\n\n<p>When testing my PCF Control in the Test Harness \u2013 I would receive a 401 unauthorised error when attempting to call the Dynamics v9.1 REST API, despite the Browser being used for the Test Harness being authenticated and so passing the correct headers into the Harness.&nbsp; This will be an area to come back to see if we can have calls to Dynamics enabled within the Test Harness to run these without deploying fully to Dynamics 365.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Functional Testing the Control in Dynamics 365<\/h3>\n\n\n\n<p>We first must connect our development of the PCF Control to a target build of Dynamics 365 \u2013 ideally a Sandbox or Dev Environment where we can work with the Control.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\"><p><strong>pac auth create<\/strong>&nbsp;&#8211;url&nbsp;<a href=\"https:\/\/[mycrm].crm11.dynamics.com\/\">https:\/\/[mycrm].crm11.dynamics.com<\/a><\/p><\/blockquote>\n\n\n\n<p>We can push our control into Dynamics via the following command.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\"><p><strong>pac pcf push<\/strong>&nbsp;&#8211;publisher-prefix crmcs<\/p><\/blockquote>\n\n\n\n<p>This will install the Control into Dynamics so we can start connecting to the Fields on Forms and have our Control presented as the UI for a Field over the typical Text Box or standard UI.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/license.citruslime.com\/cs\/blogs\/crmcs\/image_4CFB7F84.png\"><img decoding=\"async\" src=\"https:\/\/license.citruslime.com\/cs\/blogs\/crmcs\/image_thumb_3AB2B8C2.png\" alt=\"image\" title=\"image\" \/><\/a><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Build \u2013&gt; Test \u2013&gt; Rework<\/h3>\n\n\n\n<p>As with any development (particularly when learning the ropes) we will hit our main loop:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Specify \u2013 define our Inputs and Outputs in the&nbsp;<strong>ControlManifest.Input.xml<\/strong><\/li><li>Build using the&nbsp;<strong>index.ts and \u2018npm run build\u2019<\/strong><\/li><li><strong>Unit Test<\/strong>&nbsp;in the Test Harness using \u2018<strong>npm start<\/strong>\u2019<\/li><li>Functional Test by Deploying to a Sandbox using \u2018<strong>pac pcf push<\/strong>\u2019<\/li><\/ul>\n\n\n\n<p>With each deployment to our Sandbox Environment, we may see the Control fail to update or refresh on Testing.<\/p>\n\n\n\n<p>This is often where we forget to increment the Version Number in the ControlManifest.input.xml file and so the control fails to update to the new version we have built and pushed.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/license.citruslime.com\/cs\/blogs\/crmcs\/image_03D8647C.png\"><img decoding=\"async\" src=\"https:\/\/license.citruslime.com\/cs\/blogs\/crmcs\/image_thumb_23873E44.png\" alt=\"image\" title=\"image\" \/><\/a><\/figure>\n\n\n\n<p>So when at the stage of Functional Testing in Sandbox, we should keep an eye on incrementing the Version Number between releases.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Tips and Gotchas<\/h3>\n\n\n\n<ul class=\"wp-block-list\"><li>Lookup controls are currently not available for use as bound controls within PCF solutions !<\/li><li>We can make use of the normal Javasccript model in Dynamics 365 by including references to feature-usage in our ControlManifest.Input.Xml<\/li><\/ul>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/license.citruslime.com\/cs\/blogs\/crmcs\/image_3F2BCA3A.png\"><img decoding=\"async\" src=\"https:\/\/license.citruslime.com\/cs\/blogs\/crmcs\/image_thumb_0A1F3B0A.png\" alt=\"image\" title=\"image\" \/><\/a><\/figure>\n\n\n\n<ul class=\"wp-block-list\"><li>The Code for the ExampleControl used in the screenshots in this article can be downloaded here.<\/li><\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Further Reading<\/h3>\n\n\n\n<p>When building my first PCF Custom Controls and looking at what is possible, I found the following links helpful:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\"><p><strong>PCF Controls \u2013 now I have my first PCF control, too<\/strong><\/p><p><a href=\"https:\/\/www.itaintboring.com\/dynamics-crm\/pcf-controls-now-i-have-my-first-pcf-control-too\/\"><strong>https:\/\/www.itaintboring.com\/dynamics-crm\/pcf-controls-now-i-have-my-first-pcf-control-too\/<\/strong><\/a><\/p><\/blockquote>\n\n\n\n<p><a href=\"https:\/\/www.briteglobal.com\/blogs\/community\/create-naics-pcf-control-webapi\/\">https:\/\/www.briteglobal.com\/blogs\/community\/create-naics-pcf-control-webapi\/<\/a><\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\"><p><strong>Power Apps Pro Dev &amp; ISV Forum<\/strong><\/p><p><a href=\"https:\/\/powerusers.microsoft.com\/t5\/Power-Apps-Pro-Dev-ISV\/bd-p\/pa_component_framework\"><strong>https:\/\/powerusers.microsoft.com\/t5\/Power-Apps-Pro-Dev-ISV\/bd-p\/pa_component_framework<\/strong><\/a><strong>&nbsp;<\/strong><\/p><p><strong>D365Spartan &#8211; Good Breakdown of the ControlManfest.Input.Xml<\/strong><\/p><p><a href=\"https:\/\/d365spartan.wordpress.com\/2019\/08\/07\/pcf-exploring-controlmanifest-file-in-detail\/\"><strong>https:\/\/d365spartan.wordpress.com\/2019\/08\/07\/pcf-exploring-controlmanifest-file-in-detail\/<\/strong><\/a><\/p><\/blockquote>\n","protected":false},"excerpt":{"rendered":"<p>Even as a long standing Dynamics Developer and Architect I\u2019d held back slightly from getting into Custom PCF Development as one look at the steps involved gave me pause. This is because the initial steps looked like a decent barrier to entry \u2013 as the initial start is heavily dependent on compiling via command line\u00a0and\u00a0npm.\u00a0<\/p>\n","protected":false},"author":43,"featured_media":35,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_price":"","_stock":"","_tribe_ticket_header":"","_tribe_default_ticket_provider":"","_tribe_ticket_capacity":"0","_ticket_start_date":"","_ticket_end_date":"","_tribe_ticket_show_description":"","_tribe_ticket_show_not_going":false,"_tribe_ticket_use_global_stock":"","_tribe_ticket_global_stock_level":"","_global_stock_mode":"","_global_stock_cap":"","_tribe_rsvp_for_event":"","_tribe_ticket_going_count":"","_tribe_ticket_not_going_count":"","_tribe_tickets_list":"[]","_tribe_ticket_has_attendee_info_fields":false,"footnotes":""},"categories":[3],"tags":[],"class_list":{"0":"post-219","1":"post","2":"type-post","3":"status-publish","4":"format-standard","5":"has-post-thumbnail","7":"category-crm"},"featured_image_src":"https:\/\/blog.citrus-lime.com\/crmc\/wp-content\/uploads\/sites\/30\/2021\/11\/Dynamics-365-Consultancy.jpg","author_info":{"display_name":"jadesmith","author_link":"https:\/\/blog.citrus-lime.com\/crmc\/author\/jadesmith\/"},"_links":{"self":[{"href":"https:\/\/blog.citrus-lime.com\/crmc\/wp-json\/wp\/v2\/posts\/219","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.citrus-lime.com\/crmc\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.citrus-lime.com\/crmc\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.citrus-lime.com\/crmc\/wp-json\/wp\/v2\/users\/43"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.citrus-lime.com\/crmc\/wp-json\/wp\/v2\/comments?post=219"}],"version-history":[{"count":1,"href":"https:\/\/blog.citrus-lime.com\/crmc\/wp-json\/wp\/v2\/posts\/219\/revisions"}],"predecessor-version":[{"id":220,"href":"https:\/\/blog.citrus-lime.com\/crmc\/wp-json\/wp\/v2\/posts\/219\/revisions\/220"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.citrus-lime.com\/crmc\/wp-json\/wp\/v2\/media\/35"}],"wp:attachment":[{"href":"https:\/\/blog.citrus-lime.com\/crmc\/wp-json\/wp\/v2\/media?parent=219"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.citrus-lime.com\/crmc\/wp-json\/wp\/v2\/categories?post=219"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.citrus-lime.com\/crmc\/wp-json\/wp\/v2\/tags?post=219"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}