Communication between React PCF controls and the form

This issue

The PCF framework supports communication between the PCF and form via binding to a field or dataset. However, what if more comprehensive communication is required?

This post describes (at a high level) a custom solution to implement a more comprehensive communication solution between React based PCFs and the form. To help describe this, the following configuration is used (Figure 1):

  • two PCF controls ‘Person Type’ & ‘Ethnicity’
  • the Person Details form’s JavaScript files
    • personDetails.js
    • culturalIdentityHistory.js

An overview of the solution

Figure 2

The key aspect of the custom solution is the ‘Mediation between PCF controls and form’ component (Figure 2). This component stores the following information

  • event Listeners (each event listener allows the form’s JavaScript to communicate with a specific PCF on the form. One event listener for each PCF)
    • configurationid (the id of the PCF)
    • event handler
    • getControl (a reference to the PCF object)
  • form PCF services (each form PCF service allows each PCF to communicate with the form’s JavaScript. One form PCF service for each JavaScript file)

This mediation component ‘Mediation between PCF controls and form’ (Figure 2) is defined by a class called pcfControlFormAdaptor (Appendix 2). The class instance is stored in the windows DOM so that the PCFs have access to it.

The Person Detail’s form

This section describes how the PcfControlFormAdaptor instance is created and populated

The PCFs on the form

a.) During PCF initialisation, the PcfBaseControl’s onChildComponentEvent() function (Appendix 1) is called, passing in the event ‘Initialization’ (explained in https://dustinminer.com/2023/12/06/implementing-the-componentdidmount-method-in-a-react-pcf-control/).

b.) To handle this event, onChildComponentEvent() waits for the pcfControlFormAdaptor class to be instantiated (as explained in ‘The form’ section below).

c.) Once the class is instantiated, onChildComponentEvent() implements the handling of the event by creating an event listener object(amongst other things) (Appendix 2). This is stored within the instantiated PcfControlFormAdaptor object. Also stored in this object is a reference to the control’s object e.g. MultiSelectControl.

In the example this blog post describes, two event listeners are created (for the two PCF controls)

The form

When the form’s onload event handler executes, it calls Common’s LoadWebResources() function. This loads all specified JavaScript files including pcfControlFormAdaptor.js. The instantiated PcfControlFormAdaptor object is then assigned to the top level of the windows DOM

In the example that this blog post describes, the other files that are loaded are

  • personDetails.js
  • culturalIdentityHistory.js

This results in

a.) The PersonDetails object calling PcfControlFormAdaptor’s setPcfFormServce() to store a reference to it’s pcfService() function in the PcfControlFormAdaptor object

b.) The culturalIdentityHistory object also calling PcfControlFormAdaptor’s setPcfFormService() to store a reference to it’s pcfService() function in the PcfControlFormAdaptor object

Scenarios

Figure 3
1.) From the PCF to the form
Overview

Consider when a user selects ‘Person Type’ = client. When this occurs, an alert dialog (confirming the selection) is generated on the form

Steps

a.) When a user selects ‘Person Type’ = client, an event ‘pre.option.selection.change’ is generated by a React component (not described in this post) and sent to the PcfBaseControl’s onChildComponentEvent() function

b.) The onChildComponentEvent() function retrieves the PcfControlFormAdaptor object.

c.) The PcfControlFormAdaptor’s formPcfServices() function is called. This causes the event to be sent to the two pcfService() functions

d.) The PersonDetails’ pcfService() function handles the event by generating an alert. (The CulturalIdentityHistory’s pcfService() function is also called but not code exists to handle the event.)

2.) From the form to the PCF 1/2
Overview

Consider when a user selects ‘Person Type’ = client (i.e. same as the previous scenario). When this occurs, the control’s option set list is re-evaluted (Figure 3)

Steps

a.) when a user selects ‘Person Type’ = client, the onChange event for ‘Person Type’ (registered in personDetails.js) is triggered

b.) the PersonDetail’s setPersonTypePcfControl() function is called by the onChange event. This function

  • creates an updated option list based on rules such as the type of user logged it, which values have been populated on the form etc…
  • this function in turn calls the PcfControlFormAdaptor’s updateOptionList() function which
    • retrieves the relevant PCF control event handler (i.e. handlePcfAdaptorEvent()) from the PcfControlFormAdaptor object
    • creates an event ‘option.list.change’
    • sends the event to PcfBaseControl’s handlePcfAdaptorEvent() function with the updated option list

c.) In the PCF, handlePcfAdaptorEvent() calls the PcfBaseControl’s UpdateOptionList() function

3.) From the form to the PCF 2/2
Overview

In this example, the user clears the selected value in ‘Person Type’. This causes the selected value in the Ethnicity PCF control to be cleared (Figure 3)

Steps

a.) when a user clears the selected value in ‘Person Type’, the onChange event for ‘Person Type’ (registered in culturalIdentityHistory.js) is triggered

b.) the CulturalIdentityHistory’s setEthnicityValues() function is called by the onChange event. This function

  • retrieves the ‘Ethnicity’ PCF control by calling PcfControlFormAdaptor’s getControl() function.
  • creates an event ‘clear.value’
  • sends the event to the PcfBaseControl’s controlService() function

c.) In the PCF, controlService() calls index.js’s processControlEvent(). This function clears the value in the ethnicity control

Appendix

1.) PCF layer

  • class MultiSelectControl extends PcfControlBase(index.ts)
  • class MultiLookupControl extends PcfControlBase (index.ts)
    • processControlEvent()
  • class PcfControlBase (pcfControlBase.ts) implements ComponentFramework.StandardControl
    • updateOptionList()
    • registerPcfAdaptorEventListener() wraps PcfControlFormAdaptor’s registerPcfAdaptorEventListener()
    • onChildComponentEvent()
    • handlePcfAdaptorEvent()
    • controlService() calls MultiLookupControl’s processControlEvent()

2.) Mediation layer

  • class PcfControlFormAdaptor (pcfControlFormAdaptor.ts)
    • setFormPcfService()
    • formPcfService()
    • registerPcfAdaptorEventListener()
    • controlService() implemented in PcfControlBase
    • updateOptionList()
    • eventListeners[]
      • configurationId
      • eventHandler
    • getControl() retrieves the MultiLookupControl object

3.) Form layer

  • class Common (common.ts)
    • LoadWebResources()
  • class PersonDetails (personDetails.ts)
    • setPersonTypePcfControl()
    • pcfService()
  • class CulturalIdentityHistory (culturalIdentityHistory.ts)
    • setEthnicityValues()
    • pcfService()

Note:

  • Webpack wasn’t used
  • window.document contains the PCF controls
  • window.document.iframe contains the forms